@StrGlee
        
        2018-06-18T04:43:07.000000Z
        字数 4320
        阅读 10957
    logstash 时区 时间戳 timestamp
logstash 时区和时间问题让很多人头疼,今天就说说它吧。
日期过滤器用于分析字段中的日期,然后使用该日期或时间戳作为事件的logstash时间戳。
该插件支持以下配置选项
| Setting | Input type | Required | Default | 
|---|---|---|---|
| locale | string | No | No | 
| match | array | No | [] | 
| tag_on_failure | array | No | ["_dateparsefailure"] | 
| target | string | No | "@timestamp" | 
| timezone | string | No | No | 
使用IETF-BCP47或POSIX语言标记指定用于日期分析的语言环境。en,en-US用于BCP47或者en_US用于POSIX。 
当解析月份名称(MMM模式)和星期几名称(EEE模式)时,如果默认语言环境不是english,需要设置 
date {locale => "en"...}
对于非格式化语法,您需要在值的周围放置单引号字符。例如,如果你解析ISO8601时间,“2018-06-01T01:12:23”,你需要这样写:
date {match => ["log_timestamp", "yyyy-MM-dd'T'HH:mm:ss"]}
如果你需要匹配多种时间格式,如 "2018-06-01T01:23:05Z", "1529122935988", "1529122935",你需要这样写:
date {match => ["log_timestamp", "ISO8601", "UNIX", "UNIX_MS"]}
ISO8601 
类似 "2018-06-17T03:44:01.103Z" 这样的格式。具体Z后面可以有 "08:00"也可以没有,".103"这个也可以没有。常用场景里来说,Nginx 的 log_format 配置里就可以使用 $time_iso8601 变量来记录请求时间成这种格式。
UNIX 
UNIX 时间戳格式,记录的是从 1970 年起始至今的总秒数。Squid 的默认日志格式中就使用了这种格式。
UNIX_MS 
这个时间戳则是从 1970 年起始至今的总毫秒数。据我所知,JavaScript 里经常使用这个时间格式。
TAI64N 
TAI64N 格式比较少见,是这个样子的:@4000000052f88ea32489532c。我目前只知道常见应用中,qmail 会用这个格式。
Joda-Time 库 
Logstash 内部使用了 Java 的 Joda时间库来作时间处理。所以我们可以使用Joda库所支持的时间格式来作具体定义。 
Joda时间格式定义见下表
| Symbol | Meaning | Presentation | Examples | 
|---|---|---|---|
| G | era | text | AD | 
| C | century of era (>=0) | number | 20 | 
| Y | year of era (>=0) | year | 1996 | 
| x | weekyear | year | 1996 | 
| w | week of weekyear | number | 27 | 
| e | day of week | number | 2 | 
| E | day of week | text | Tuesday; Tue | 
| y | year | year | 1996 | 
| D | day of year | number | 189 | 
| M | month of year | month | July; Jul; 07 | 
| d | day of month | number | 10 | 
| a | halfday of day | text | PM | 
| K | hour of halfday (0~11) | number | 0 | 
| h | clockhour of halfday (1~12) | number | 12 | 
| H | hour of day (0~23) | number | 0 | 
| k | clockhour of day (1~24) | number | 24 | 
| m | minute of hour | number | 30 | 
| s | second of minute | number | 55 | 
| S | fraction of second | number | 978 | 
| z | time zone | text | Pacific Standard Time; PST | 
| Z | time zone offset/id | zone | -0800; -08:00; America/Los_Angeles | 
| ' | escape for text | delimiter | |
| '' | single quote | literal | ' | 
| example | match | 
|---|---|
| 2018-06-01T01:23:05+08:00 | yyyy-MM-dd'T'HH:mm:ss+08:00 或 ISO8601 | 
| 2018-06-01T01:23:05Z | yyyy-MM-dd'T'HH:mm:ssZ 或 ISO8601 | 
| Jun 07 2018 01:23:05 | MMM dd yyyy HH:mm:ss (注: locale => "en") | 
| Jun 7 2018 01:23:05 | MMM d yyyy HH:mm:ss (注: locale => "en") | 
| 1529122935988 | UNIX_MS | 
| 1529122935 | UNIX | 
如果匹配失败,将值附加到tag字段
将匹配的时间戳存储到给定的目标字段中。如果未提供,则默认更新事件的@timestamp字段
当需要配置的date里面没有时区信息,而且不是UTC时间,需要设置timezone参数。 
比如匹配北京时间 "2018-06-18 11:10:00",则需要设置
date {match => ["logdate", "yyyy-MM-dd HH:mm:ss"]timezone => "Asia/Chongqing"}
| Standard Offset | Canonical ID | Aliases | 
|---|---|---|
| +08:00 | Asia/Chongqing | Asia/Chungking | 
| +08:00 | Asia/Hong_Kong | Hongkong | 
| +08:00 | Asia/Shanghai | PRC | 
附: timezone列表
在ELK组合中我们在outputs/elasticsearch中常用的%{+YYYY.MM.dd}来创建索引,而这种写法是必须要读@timestamp这个字段的。默认情况下@timestamp字段显示的是当前时间,但我们可能需要记录的是日志中的字符串类型的时间,所以我们需要把日志中字符串类型的时间覆盖掉@timestamp中的当前时间。
input {stdin{}}filter {grok {match => ["message", "%{TIMESTAMP_ISO8601:logdate}"]}date {match => ["logdate", "yyyy-MM-dd HH:mm:ss.SSS"]target => "@timestamp"}mutate {remove => ["logdate"]}}output{stdout{codec=>rubydebug{}}}
附: logstash自带的正则 logstash-patterns
处理逻辑如下
input { stdin {} }output { stdout { codec => rubydebug } }filter {date {match => ["message","UNIX_MS"] # message在实际应用中修改为自己的字段target => "@timestamp"}ruby {code => "event['timestamp'] = LogStash::Timestamp.new(event['@timestamp']+ 8*60*60)"}ruby {code => "event['@timestamp']= event['timestamp']"}mutate {remove_field => ["timestamp"]}}
input { stdin {} }output { stdout { codec => rubydebug } }filter {date {match => ["message","UNIX_MS"]target => "@timestamp"}ruby {code => "event.set('timestamp', event.get('@timestamp').time.localtime + 8*60*60)"}ruby {code => "event.set('@timestamp',event.get('timestamp'))"}mutate {remove_field => ["timestamp"]}}
# 1. 增加一个字段,计算timestamp+8小时ruby {code => "event.set('index_date', event.get('@timestamp').time.localtime + 8*60*60)"}# 2. 用mutate插件先转换为string类型,gsub只处理string类型的数据,在用正则匹配,最终得到想要的日期mutate {convert => ["index_date", "string"]gsub => ["index_date", "T([\S\s]*?)Z", ""]gsub => ["index_date", "-", "."]}# 3.output配置elasticsearch {hosts => ["localhost:9200"]index => "myindex_%{index_date}"}
很多中国用户经常提一个问题:为什么 @timestamp 比我们早了 8 个小时?怎么修改成北京时间?
其实,Elasticsearch 内部,对时间类型字段,是统一采用 UTC 时间,存成 long 长整形数据的!对日志统一采用 UTC 时间存储,是国际安全/运维界的一个通识——欧美公司的服务器普遍广泛分布在多个时区里——不像中国,地域横跨五个时区却只用北京时间。
对于页面查看,ELK 的解决方案是在 Kibana 上,读取浏览器的当前时区,然后在页面上转换时间内容的显示。
所以,建议大家接受这种设定。否则,即便你用 .getLocalTime 修改,也还要面临在 Kibana 上反过去修改,以及 Elasticsearch 原有的 ["now-1h" TO "now"] 这种方便的搜索语句无法正常使用的尴尬。