Logstash在处理数据的时候,会自动生成一个字段@timestamp
,默认该字段存储的是Logstash收到消息/事件(event)的时间。很多时候我们用ELK是处理日志的,日志里面一般都是有时间的。而且很多时候我们只关注日志里面的时间,而不关注Logstash收到这条日志的时间。这个时候,一种方法是再增加一个字段,用来存储日志里面的时间,这种很简单;另一种方法是使用日志中的时间替换掉@timestamp
字段默认的时间。本文介绍第二种方法并总结一些关键知识点。
现在有如下一条日志:
2018-02-26 15:48:32.708-[INFO ] main RestfulApiProvider - initializing restful api provider
我们先用最简单的Logstash规则解析,规则文件test.conf如下:
# 从标准输入读入数据
input { stdin {} }
# 只解析时间戳,其它信息不管
filter {
grok {
match => { "message" => "(?<timestamp>%{TIMESTAMP_ISO8601})" }
}
}
# 输出到标准输出
output {
stdout { codec => rubydebug }
}
启动Logstash:bin/logstash -f test.conf
在标准输入粘贴上面的日志并回车,输出如下:
{
"host" => "NiYanchuns-MacBook-Air.local",
"@timestamp" => 2018-10-18T12:20:51.603Z,
"timestamp" => "2018-02-26 15:48:32.708",
"message" => "2018-02-26 15:48:32.708-[INFO ] main RestfulApiProvider - initializing restful api provider",
"@version" => "1"
}
这里需要注意以下几个点:
- 解析规则里面的grok是Logstash的一个正则解析插件,Logstash强大的解析能力主要来自于它,使用文档看这里。
@timestamp
字段是内置的,和之前说的一样,时间是Logstash收到消息的时间,而且注意使用的是UTC时间,我电脑的时间是北京时间晚上八点多。timestamp
字段是我们解析规则里面定义的字段(字段名随便起,不能有特殊符号,比如@
),该字段存储的是从日志里面解析出来的时间,注意这个时间格式完全是日志里面时间的格式。要注意我们加的timestamp
和系统内置的@timestamp
不是同一个字段。message
:也是程序内置字段,内容为消息原始内容。
既然如此,那我们能不能通过自定义一个与程序内置的@timestamp
同名的字段来覆盖掉程序内置字段呢?比如将上面解析规则里面的timestamp
改为@timestamp
。
答案是不行。刚才已经说了,系统内置字段名前可以加@
,但我们定义字段的时候字段名不能加@
,也就是说我们不能通过定义一个@timestamp
字段覆盖掉系统默认的,那样配置语法检查就通不过。有(bu)兴(xin)趣(xie)的可以试一下。
那如何做呢?Logstash提供了一个Date插件可以实现该功能,使用文档见这里。我们修改一下刚才的规则:
input { stdin {} }
filter {
grok {
match => { "message" => "(?<timestamp>%{TIMESTAMP_ISO8601})" }
}
date {
match => [ "timestamp", "ISO8601" ]
}
}
output {
stdout { codec => rubydebug }
}
重启Logstash后,输入日志后,解析输出如下:
{
"@version" => "1",
"host" => "NiYanchuns-MacBook-Air.local",
"timestamp" => "2018-02-26 15:48:32.708",
"@timestamp" => 2018-02-26T07:48:32.708Z,
"message" => "2018-02-26 15:48:32.708-[INFO ] main RestfulApiProvider - initializing restful api provider"
}
OK,@timestamp
字段里面的值已经和timestamp
字段很像了,但不完全一样。Logstash将timestamp
的时间根据系统的时区转换为UTC时间存到了@timestamp
字段里面。
这个时候timestamp
就成多余的了,我们可以通过mutate
插件移除该字段。再次修改解析规则文件:
input { stdin {} }
filter {
grok {
match => { "message" => "(?<timestamp>%{TIMESTAMP_ISO8601})" }
}
date {
match => [ "timestamp", "ISO8601" ]
}
mutate {
remove_field => [ "ts_tmp","date", "time", "end_time_tmp", "end_time_tmp1" ]
}
}
output {
stdout { codec => rubydebug }
}
重启Logstash后,输入日志后,解析输出如下:
{
"message" => "2018-02-26 15:48:32.708-[INFO ] main RestfulApiProvider - initializing restful api provider",
"@version" => "1",
"@timestamp" => 2018-02-26T07:48:32.708Z,
"host" => "NiYanchuns-MacBook-Air.local"
}
timestamp
字段已经在输出中去掉了。
我们简单介绍一下Date
插件。Date常见的配置如下:
date {
match => [ "time_field", "yyyyMMdd HH:mm:ss.SSS" ]
# timezone => "UTC"
target => "end_time"
}
上述配置的含义是,将time_field
字段按照yyyyMMdd HH:mm:ss.SSS
格式解析后存到target指定的字段end_time
字段去。time_field
必须是已经定义的字段,最常见的就是在grok里面解析出来的某个时间字段。时间格式可查看Date插件的文档。如果没有指定target,默认就是@timestamp
字段,这就是为什么我们可以使用该插件来修改@timestamp
字段值的原因。
另外,timezone
字段在某些场景下也非常重要,如果从时间的值里面解析不出来时区,而且我们也没有指定时区的话,程序就会认为我们的时间字段的时区就是系统所处时区。比如上面从timestamp
转到@timestamp
的时候,时间值里面没有时区,所以使用了系统的时区东八区。当然,我们可以使用该字段指定时区。
OK,本文就到这里,干(hong)正(wo)事(wa)去了。
是我看花眼了吗,@timestamp始终没见转换为实际业务时间啊
变了呀,业务时间就指的是日志里面的时间,默认是采集时间
果然可以,看了半天文档竟然没有发现这个插件,也是醉了……
主要原因还是按照最佳实践都应该用UTC时间,所以需求按照最佳实践来说不是很重要
这个时间格式可以改的吧
@timestamp的格式好像是不能改的,至少Date插件里面没有提供修改的方式。
这个我前两天也刚好搞了。。
个人觉得这个东西应该让用户可以指定,虽然用
date
插件可以实现,但还是麻烦学习,谢谢分享
客气~