Kibana连接ES查询数据的时候,会有时差8小时的问题。先来描述一下问题的具体情况,我们先来看看Logstash默认写入到ES的索引数据。timestamp是我们App上报日志的时间戳字段,这个字段是客户端写入日志的时间。@timestamp是使用Logstash写入ES的时候默认自带的时间戳(即Logstash生成ES索引的时间戳)。这里我们的这条日志是2016-10-08由App记录的,由Logstash收集到ES服务器的时间是2016-12-05。但是我们发现@timestamp并不是我们系统的当前时间,而是比我们当前的系统时间小了8小时,这就是我们想要解决的8小时时差问题。
原因是Logstash中默认插入的@timestamp时间字段是按照UTC 00:00标准时区的时间转换成long型的时间戳保存在ES中,而我们系统的时间是中国时区UTC +08:00,由ES查询出来的long型的时间戳再按照UTC +08:00转换成时间就正好相差了8小时。因为@timestamp的long型字段在ES中是不可取回的,所以我们在ES的返回值中是看不到这个long型字段的,只能看到@timestamp根据我们的时区转换后的结果”2016-12-05T11:30:40.911Z”,这个结果正好和我们的系统时间相差了8小时。
|
|
上面简单的描述了一下问题。我们需要解决下面两个问题:
- 如何让Kibana查询和统计使用的是App写入日志的时间,而不是Logstash写入ES的时间(因为Logstash写入ES时间上会有延迟)
- 解决时差相差8小时问题
一般我们App上报日志都会带有一个timestamp时间戳字段,这个字段是客户端写入日志的时间。当Logstash收集App上报日志的时候,会将timestamp字段保存到ES中,Kibana通过该字段当做统计的字段进行各种按日期统计的查询。
这里Kibana要求所有ES的索引必须要有一个时间字段作为统计查询的日期字段使用,如果没有ES的索引没有时间或者日期字段,是无法在Kibana中创建索引的。所以默认Kibana也会给一个默认的时间字段@timestamp,这样当我们在Kibana创建api_logs_index索引的时候,就会出现有两个时间字段,一个是timestamp,一个是@timestamp。
在Kibana创建索引时,设置索引使用@timestamp字段,但是需要在Logstash中配置@timestamp的值从timestamp取出来的。Logstash中可以指定@timestamp字段的值是从App上报日志的timestamp字段来的。
|
|
下面用我们这条日志举例,下面的表格是系统时间和UTC时间戳根据北京时区的转换对照表。这里的系统时间转换程UTC时间戳是带有北京时区的。
系统时间 | UTC时间戳 | ES返回(显示)的时间 | ES存储的时间(long) |
---|---|---|---|
2016/10/18 15:45:15.001Z | 1475912715001 | timestamp : 1475912715001 | |
2016/10/18 7:45:15.001Z | 1475883915001 | timestamp : 2016/10/18 7:45:15.001Z | @timestamp : 1475883915001 |
2016/10/17 23:45:15.001Z | @timestamp : 2016/10/17 23:45:15.001Z |
Logstash将timestamp的时间2016/10/18 7:45:15.001Z按照默认的标准时区UTC 00:00将timestamp转换成long类型1475912715001存储到ES,而对于@timestamp字段的值,是将timestamp的时间2016/10/18 7:45:15.001Z按照北京时区UTC +08:00将timestamp转换成long类型1475883915001并且赋值给@timestamp并且存储到ES。(为什么要加上北京时区UTC +08:00的原因不清楚,也尝试过不带有Z时区设置的时间格式,转换成的时间戳结果一样)
|
|
ES存储的时间long型到ES返回(显示)时间是按照UTC 00:00标准时间转换的。下面是取回的结果。
- ES将timestamp的long类型1475912715001按照UTC 00:00标准时间转换回时间2016/10/18 7:45:15.001Z
- ES将@timestamp的long类型1475883915001按照UTC 00:00标准时间转换回时间2016-10-07T23:45:15.001Z
|
|
这里可以看到Kibana的查询Request,下面是我选择的时间区间条件
- 2016/10/8 7:30:0:按照北京时区UTC +08:00转换为1475883000000
- 2016/10/8 8:0:0:按照北京时区UTC +08:00转换为1475884800000
Kibana Request
|
|
上面的Request可以看出来我们Kibana Request是将查询时间区间的条件先按照Browser的时区(北京时区UTC +08:00)转换成long型时间戳,然后当成条件查询的@timestamp字段。
这里的fields就是ES存储的long类型数值。
- timestamp 1475912715001:按照标准时区UTC +00:00转换为2016/10/8 7:45:15
- @timestamp 1475883915001:按照标准时区UTC +00:00转换为2016/10/7 23:45:15
|
|
Kibana Response
|
|
ES返回的结果时间是按照标准时区UTC +00:00转换的。这里Kibana还会根据创建索引所选择的时间戳,再将@timestamp的结果转换成Browser默认的时区(即:UTC +08:00)显示出来,也就是在ES返回的@timestamp时间上再加8小时。
如果timestamp字段没有带有时区设置,我们可以在Logstash中指定locale和timezone属性来设置timestamp的字段的时区,这样就知道timestamp的时间应该按照指定的timezone时区解析成long型时间戳,这样my_timestamp字段在ES中是一个使用+08:00中国时区的long类型时间戳,方便使用ES API直接查询timestamp字段,因为一般人都使用+08:00中国时区将时间转换为long类型的时间戳。
|
|
Logstash创建索引所使用的YYYY-MM-DD也是使用的@timestamp字段的时间
参考文章: