1. Date Filter 插件
- 日期过滤器用于分析字段中的日期,然后使用该日期或时间戳作为事件的 logstash 时间戳。
1.1. 配置项
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 |
1.1.1 locate
-
Value type is string
-
There is no default value for this setting.
-
使用 IETF-BCP47 或 POSIX 语言标记指定用于日期分析的语言环境。en,en-US 用于 BCP47 或者 en_US 用于 POSIX。
-
当解析月份名称(MMM模式)和星期几名称(EEE模式)时,如果默认语言环境不是 english,需要设置:
date { locale => "en" ... }
1.1.2. match
-
Value type is array
-
Default value is []
-
对于非格式化语法,您需要在值的周围放置单引号字符。例如,如果你解析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"] }
-
date 插件支持五种时间格式:
- 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
1.1.3. tag_on_failure
- Value type is array
- Default value is ["_dateparsefailure"]
如果匹配失败,将值附加到 tag 字段。
1.1.4. target
- Value type is string
- Default value is "@timestamp"
- 将匹配的时间戳存储到给定的目标字段中。如果未提供,则默认更新事件的@timestamp字段。
1.1.5. timezone
-
Value type is string
-
There is no default value for this setting.
-
当需要配置的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列表
2. logstash @timestamp自定义
- 在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
3. @timestamp 时间少 8 小时
3.1. 5.0 以下版本
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"]
}
}
3.2. 5.x 以上版本
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"]
}
}
4. index 索引名称少 8 小时
# 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}"
}
5. 时区问题的解释
-
很多中国用户经常提一个问题:为什么 @timestamp 比我们早了 8 个小时?怎么修改成北京时间?
-
其实,Elasticsearch 内部,对时间类型字段,是统一采用 UTC 时间,存成 long 长整形数据的!对日志统一采用 UTC 时间存储,是国际安全/运维界的一个通识——欧美公司的服务器普遍广泛分布在多个时区里——不像中国,地域横跨五个时区却只用北京时间。
-
对于页面查看,ELK 的解决方案是在 Kibana 上,读取浏览器的当前时区,然后在页面上转换时间内容的显示。
-
所以,建议大家接受这种设定。否则,即便你用 .getLocalTime 修改,也还要面临在 Kibana 上反过去修改,以及 Elasticsearch 原有的 ["now-1h" TO "now"] 这种方便的搜索语句无法正常使用的尴尬。