Logstash中建ES索引的时区问题

最近工作中遇到一个Logstash中建ES索引的时区问题,对数据统计造成了一定的影响。

logstash.conf文件(简化了业务代码):

input{
  ...
}

filter{
  date {
    match => ["access_time", "yyyy/MM/dd HH:mm:ss Z"]
    target => "@timestamp"
  }
}
output {
     #stdout { codec => rubydebug }
     elasticsearch {
         hosts => [...]
         index => "log-%{+YYYY.MM.dd}"
     }
}

 

问题描述:

我们把日志中的时间存进变量access_time字段,按天建立ES中的索引统计日志,预想情况是例如access_time为2020.12.15的日志都存在ES的log-2020.12.15索引中。

我们后续采用Grafana查询ES,比如根据@timestamp查某个时间范围内的数据。由于logstash会默认把@timestamp转换成UTC时间(格林威治标准时间),我们专门进行了时间转换,把日志中的access_time字段(北京时间)通过match的格式匹配转换,使得@timestamp字段也是北京时间。此时日志中的access_time与logstash中的@timestamp完全一致,建ES索引时,%{+YYYY.MM.dd}会根据@timestamp字段的值取出年月日。

 

看似很顺利,但问题出现了。Elasticsearch 内部,对时间类型字段,是统一采用 UTC 时间,存成 long 长整形数据的!对日志统一采用 UTC 时间存储,是国际安全/运维界的一个通识——欧美公司的服务器普遍广泛分布在多个时区里——不像中国,地域横跨五个时区却只用北京时间。因此ES在建索引时会把@timestamp这个时间类型字段又转成UTC时间(-8小时)。

此时数据就会出现统计偏差,access_time为 2020/12/15 00:00 — 2020/12/15 8:00 之间的日志数据,本该存放在log-2020.12.15索引下,但由于时区转换问题,ES读取的@timestamp被减了8小时,为 2020/12/14 16:00 — 2020/12/15 00:00,导致这些日志数据会被统计到log-2020.12.14索引下。

而log-2020.12.15索引下统计的日志,实际上它们的access_time字段是在 2020/12/15 8:00 — 2020/12/16 8:00

 

  图1

 

 

 图2

如图1所示,对于页面查看,ELK 的解决方案是在 Kibana 上,读取浏览器的当前时区,然后在页面上转换时间内容的显示。

如图2所示,我们可以看到图2中Kibana已经自动把@timestamp转成了北京时间。这是一条access_time为 2020/12/15 06:00:00的日志,@timesstamp与access_time一致,但却是统计在log-2020.12.14索引中。

 

解决方法:

其实Kibana上看到的@timestamp已经是正确的了(与日志中的access_time一致),我们只需要解决索引的问题。

由于@timestamp字段是正确的,我们不改变该字段,而是用一个变量index_day代替@timestamp去建立索引

这个变量中存储的时间是@timestamp+8小时,这样ES对于这个时间变量index_day就会-8小时,此时index_day在ES中存储的值就是北京时间,相当于建立索引时以北京时间为依据,也就对上了。

 

input{
  ...
}

filter{
  date {
    match => ["access_time", "yyyy/MM/dd HH:mm:ss Z"]
    target => "@timestamp"
  }
   ruby{
       code => "event.set('index_day', (event.get('@timestamp').time.localtime + 8*60*60).strftime('%Y.%m.%d'))"
   }
}
output {
     #stdout { codec => rubydebug }
     elasticsearch {
         hosts => [...]
index => "log-%{index_day}" #index
=> "log-%{+YYYY.MM.dd}" } }

 

如果想及时知道创建的索引名字是否正确,可以先使用strftime('%Y.%m.%d.%H')替换strftime('%Y.%m.%d')进行测试。此时ES中索引的名字会显示小时,可以根据小时是否为北京时间判断是否更改成功。

posted @ 2020-12-17 14:36  厚植根本  阅读(1925)  评论(0编辑  收藏  举报