用filebeat+logstash 去处理日志中的timestamp问题

3,707 阅读4分钟

前言

先简单介绍下背景:使用filebeat收集日志输出到logstash进行数据处理,然后输出到elasticSearch,最终在kibana展示。 filebeat在log文件中读取的每一条日志都转化成了字段‘message’的值,然后filebeat本身会生成一些额外的key如 version、timestamp等等,这个timestamp是filebeat插入logstash的时间,实际上我们想要这个日志打印的时间,也就是字段‘message’中的时间,所以我们用到了logstash的filter功能:将message中的时间提取出来并替换掉timestamp的时间。

正文

先梳理下我项目中的流程: Filebeat-->Logstash-->ElasticSearch-->kibana.

上述的几个软件安装启动就不说了,我在启动Logstash的时候还发生过问题,可以看我另外一篇文章

这里要注意的主要是几个配置文件,第一个是filebeat, filebeat.yml是filebeat的主配置,路径为 /etc/filebeat/filebeat.yml,其中主要配置input 和output的信息,如果用到了kibana就setup。给个例子:

filebeat.inputs:
- type: log
  enabled: true
  stream: stdout
  tail_files: true
  paths:
    - '/opt/filebeat/xxx.log'

filebeat.config.modules:
  # Glob pattern for configuration loading
  path: ${path.config}/modules.d/*.yml
  # Set to true to enable config reloading
  reload.enabled: false

setup.kibana:
  # Kibana Host
  # Scheme and port can be left out and will be set to the default (http and 5601)
  # In case you specify and additional path, the scheme is required: http://localhost:5601/path
  # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
  host: "xx.xxx.xxx.xx:9090"
  
output.logstash:
  # The Logstash hosts
  hosts: ["localhost:5044"]

filebeat的output很多,这里只是用Logout举个例子,不多说。

然后就是Logstash的配置文件,也是重头戏。 logstash的配置文件在 /etc/logstash下面,其中有一个名叫logstash.yml是他的主配置文件,你还能看到一个叫conf.d的文件夹,这里面才是具体的配置,需要我们自己创建配置文件,以.conf结尾。所以第一步就是需要在logstash.yml中配置config路径。我们需要加上这个配置:path.config: /etc/logstash/conf.d/*.conf 然后再去conf.d下面去创建配置文件。贴一个例子:

input {
  beats {
    #这里是从filebeat接受数据 也支持直接从文件导入数据
    port => 5044
    #数据类型
    type => "logs"
  }
}
filter {
  grok {
        match => ["message", "%{TIMESTAMP_ISO8601:logdate}"]
  }
  date {
        match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS"]
        target => "@timestamp"
  }
}
output {
  elasticsearch {
        hosts => "localhost:9200"
        index => "omega8-%{+YYYY.MM.dd}"
  }
}

上面就是一个替换timestamp的简单配置,input的数据源是从filebeat来的,也可以替换成其他的如:

input{
    file{
        path => "/var/log/nginx/access.log"
        start_position => "beginning"
        type => "nginx_access_log"
    }
}

这里比较关键的是filter,使用了grok插件和date插件做到了timestamp的转化。

filter {
  grok {
        match => ["message", "%{TIMESTAMP_ISO8601:logdate}"]
  }
  date {
        match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS"]
        target => "@timestamp"
  }
}

grok里面做的事情其实是用正则去匹配message中的时间,而这个message就是我们在日志文件中打印的一行行的log。这里的 TIMESTAMP_ISO8601 不是随随便便瞎写的,他是logstash自带的正则匹配的一种,logstash的正则文件在哪呢?在 /usr/share/logstash/vendor/bundle/jruby/2.5.0/gems/logstash-patterns-core-4.1.2/patterns下有一个名叫grok-patterns的文件,它里面就是logstash用到的正则。也就是说,你在日志中打印的时间要确实是这个格式的,他才能匹配到。一般我们打印的格式是yyyy-MM-dd HH:mm:ss.SSS的时候,上面的正则就是匹配的。当然了如果你的日志打印不满足条件,你可以通过在他的正则文件中新增自定义的正则匹配来使用。我的日志打印格式是yyyy-MMM-dd HH:mm:ss.SSS,其中的月份是英文显示,如Dec、Feb,正好logstash自己的正则中提供了对英文月份的匹配,所以我就直接用了他的。如果没有的话,各位可能需要自己写一份满足条件的正则了。下面贴出来一部分logstash自己的正则:

# Months: January, Feb, 3, 03, 12, December
MONTH \b(?:[Jj]an(?:uary|uar)?|[Ff]eb(?:ruary|ruar)?|[Mm](?:a|ä)?r(?:ch|z)?|[Aa]pr(?:il)?|[Mm]a(?:y|i)?|[Jj]un(?:e|i)?|[Jj]ul(?:y)?|[Aa]ug(?:ust)?|[Ss]ep(?:tember)?|[Oo](?:c|k)?t(?:ober)?|[Nn]ov(?:ember)?|[Dd]e(?:c|z)(?:ember)?)\b
MONTHNUM (?:0?[1-9]|1[0-2])
MONTHNUM2 (?:0[1-9]|1[0-2])
MONTHDAY (?:(?:0[1-9])|(?:[12][0-9])|(?:3[01])|[1-9])

# Days: Monday, Tue, Thu, etc...
DAY (?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)

# Years?
YEAR (?>\d\d){1,2}
HOUR (?:2[0123]|[01]?[0-9])
MINUTE (?:[0-5][0-9])
# '60' is a leap second in most time standards and thus is valid.
SECOND (?:(?:[0-5]?[0-9]|60)(?:[:.,][0-9]+)?)
TIME (?!<[0-9])%{HOUR}:%{MINUTE}(?::%{SECOND})(?![0-9])
# datestamp is YYYY/MM/DD-HH:MM:SS.UUUU (or something like it)
DATE_US %{MONTHNUM}[/-]%{MONTHDAY}[/-]%{YEAR}
DATE_EU %{MONTHDAY}[./-]%{MONTHNUM}[./-]%{YEAR}
ISO8601_TIMEZONE (?:Z|[+-]%{HOUR}(?::?%{MINUTE}))
ISO8601_SECOND (?:%{SECOND}|60)
TIMESTAMP_ISO8601 %{YEAR}-%{MONTHNUM}-%{MONTHDAY}[T ]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
TIMESTAMP_MI %{YEAR}-%{MONTH}-%{MONTHDAY}[\s]%{HOUR}:?%{MINUTE}(?::?%{SECOND})?%{ISO8601_TIMEZONE}?
DATE %{DATE_US}|%{DATE_EU}
DATESTAMP %{DATE}[- ]%{TIME}

可以看到上述所使用的TIMESTAMP_ISO8601,而我自己定义了一个TIMESTAMP_MI,其中把月份部分的正则替换了,因为他正好提供了对于英文月份的正则,就是MONTH。 date里面就是匹配格式,然后替换到timestamp中去。

Filter的功能很强大,这里只是简单的应用了一下。

到这里,对于日志中timestamp的转换问题就解决了。比较关键的一点就是对于logstash正则的使用。