二丫讲梵 二丫讲梵
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)

二丫讲梵

行者常至,为者常成
首页
  • 最佳实践
  • 迎刃而解
  • Nginx
  • Php
  • Zabbix
  • AWS
  • Prometheus
  • Grafana
  • CentOS
  • Systemd
  • Docker
  • Rancher
  • Ansible
  • Ldap
  • Gitlab
  • GitHub
  • Etcd
  • Consul
  • RabbitMQ
  • Kafka
  • MySql
  • MongoDB
  • OpenVPN
  • KVM
  • VMware
  • Other
  • ELK
  • K8S
  • LLM
  • Nexus
  • Jenkins
  • 随写编年
  • 家人物语
  • 追忆青春
  • 父亲的朋友圈
  • 电影音乐
  • 效率工具
  • 博客相关
  • Shell
  • 前端实践
  • Vue学习笔记
  • Golang学习笔记
  • Golang编程技巧
  • 学习周刊
  • Obsidian插件周刊
关于
友链
  • 本站索引

    • 分类
    • 标签
    • 归档
  • 本站页面

    • 导航
    • 打赏
  • 我的工具

    • 备忘录清单 (opens new window)
    • json2go (opens new window)
    • gopher (opens new window)
    • 微信MD编辑 (opens new window)
    • 国内镜像 (opens new window)
    • 出口IP查询 (opens new window)
    • 代码高亮工具 (opens new window)
  • 外站页面

    • 开往 (opens new window)
    • ldapdoc (opens new window)
    • HowToStartOpenSource (opens new window)
    • vdoing-template (opens new window)
GitHub (opens new window)
  • Nexus系列文章

  • Jenkins系列文章

  • ELK笔记

    • ELK

    • FileBeat

    • LogStash

      • logstash本地调试日志方法详解
      • logstash采集日志的时间格式探微
        • 1,Date Filter 插件
        • 2,配置项
          • 1,locate
          • 2,match
          • 3,tagonfailure
          • 4,target
          • 5,timezone
        • 3,几个解析实战(重点)
          • 1,标准格式ISO8601
          • 1,原始日志。
          • 2,配置文件。
          • 3,测试效果。
          • 2,带 T模式。
          • 1,原始日志。
          • 2,配置文件。
          • 3,测试效果。
          • 3,业务日志。
          • 1,原始日志。
          • 2,配置文件。
          • 3,测试效果。
          • 4,系统日志 message。
          • 1,原始日志。
          • 2,配置文件。
          • 3,测试效果。
        • 4,时区问题
      • logstash配置geoip画访问地域热图
    • ElasticSearch

    • Kibana

  • Kubernetes笔记

  • LLM专题

  • 系列专题
  • ELK笔记
  • LogStash
二丫讲梵
2019-10-18
目录

logstash采集日志的时间格式探微

文章发布较早,内容可能过时,阅读注意甄别。

logstash 的时间格式问题,是一个老大难,当然,这种难往往来自于那种日志输出不规范的情况,尤其是时间戳,各种各样的,最终来到配置这一环节的时候,就要费很大的劲儿来调配这个东东。

所以,在立项新项目的时候,首先就需要约束好日志的格式,严格按照日志规范来走。

本文就来整理一下在配置 logstash 的时候,遇到时间格式的问题,所需要用到的,注意到的配置细节。

# 1,Date Filter 插件

日期过滤器 (opens new window)用于分析字段中的日期,然后使用该日期或时间戳作为事件的 logstash 时间戳。

# 2,配置项

该插件支持以下配置选项:

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,locate

值类型是字符串,这个设置没有默认值。使用 IETF-BCP47 或 POSIX 语言标记指定用于日期分析的区域设置。 简单的例子是 en,美国的 BCP47 或者 en_US 的 POSIX。大多数情况下需要设置语言环境来解析月份名称(MMM 模式)和星期几名称(EEE 模式)。如果未指定,则将使用平台默认值,但对于非英文平台默认情况下,英文解析器也将用作回退机制。

# 2,match

用于配置具体的匹配内容规则,前半部分内容表示匹配实际日志当中的时间戳的名称,后半部分则用于匹配实际日志当中的时间戳格式,这个地方是整条配置的核心内容,如果此处规则匹配是无效的,则生成后的日志时间戳将会被读取的时间替代。

这里列出一些常见的日志中时间格式的匹配规则,亲测可以直接放到实际中使用:

时间戳 match
2018-06-01T01:23:05+08:00 yyyy-MM-dd'T'HH:mm:ss+08:00
2018-06-01T01:23:05Z yyyy-MM-dd'T'HH:mm:ssZ
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

这块儿内容在最后会专门用实际例子进行说明。

  • 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 时间库 (opens new window)来作时间处理。所以我们可以使用 Joda 库所支持的时间格式来作具体定义。
  • 时间戳详解

    用于解析日期和时间文本的语法使用字母来指示时间值(月,分等)的类型,以及重复的字母来表示该值的形式(2 位月份,全月份名称等)。以下是可用于解析日期和时间的内容:

    • y(year)

      yyyy  #全年号码。 例如:2015。
      yy    #两位数年份。 例如:2015年的15。
      
      1
      2
    • M (month of the year)

      M     #最小数字月份。 例如:1 for January and 12 for December.。
      MM    #两位数月份。 如果需要,填充零。 例如:01 for January  and 12 for Decembe
      MMM   #缩短的月份文本。 例如: Jan for January。 注意:使用的语言取决于您的语言环境。 请参阅区域设置以了解如何更改语言。
      MMMM  #全月文本,例如:January。 注意:使用的语言取决于您的语言环境。
      
      1
      2
      3
      4
    • d (day of the month)

      d   #最少数字的一天。 例如:1月份的第一天1。
      dd  #两位数的日子,如果需要的话可以填零.例如:01 for the 1st of the month。
      
      1
      2
    • H (hour of the day (24-hour clock))

      H   #最小数字小时。 例如:0表示午夜。
      HH  #两位数小时,如果需要填零。 例如:午夜00。
      
      1
      2
    • m (minutes of the hour (60 minutes per hour))

      m   #最小的数字分钟。 例如:0。
      mm  #两位数分钟,如果需要填零。 例如:00。
      
      1
      2
    • s (seconds of the minute (60 seconds per minute) )

      s    #最小数字秒。 例如:0。
      ss   #两位数字,如果需要填零。 例如:00。
      
      1
      2
    • S 秒的小数部分最大精度是毫秒(SSS)。 除此之外,零附加。

      S    #十分之一秒。例如:0为亚秒值012
      SS   #百分之一秒 例如:01为亚秒值01
      SSS  #千分之一秒 例如:012为亚秒值012
      
      1
      2
      3
    • Z 时区偏移或身份

      Z   #时区偏移,结构为HHmm(Zulu/UTC的小时和分钟偏移量)。例如:-0700。
      ZZ  #时区偏移结构为HH:mm(小时偏移和分钟偏移之间的冒号)。 例如:-07:00。
      ZZZ  #时区身份。 例如:America/Los_Angeles。 注意:有效的ID在列表中列出http://joda-time.sourceforge.net/timezones.html
      
      1
      2
      3

其他不常用的就不再详录了,诸上之外的,可参考:http://www.joda.org/joda-time/key_format.html

# 3,tag_on_failure

如果匹配失败,将值附加到 tag 字段,字段值为 _dateparsefailure,有时候我们在 kibana 当中看到日志会有这个字段,那么不用多想了,一定是时间格式匹配失败了,需要重新检查上边的 match 配置解析是否正确。

# 4,target

将匹配的时间戳存储到给定的目标字段中。如果未提供,则默认更新事件的@timestamp 字段。一般情况下,我们用的配置如下:

filter {
   date {
      match        => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z", "UNIX", "yyyy-MM-dd HH:mm:ss", "dd-MMM-yyyy HH:mm:ss" ]
      target       => "@timestamp"
   }
}
1
2
3
4
5
6

此处配置的意思是,match 对原始日志时间格式进行解析,解析之后,存储到下边定义的 target 定义的字段中,一般这个字段肯定是 @timestamp,而又因为默认字段就是@timestamp,因此这个字段可以省略,配置如下,效果一样。

filter {
   date {
      match        => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z", "UNIX", "yyyy-MM-dd HH:mm:ss", "dd-MMM-yyyy HH:mm:ss" ]
   }
}
1
2
3
4
5

# 5,timezone

当需要配置的 date 里面没有时区信息,而且不是 UTC 时间,需要设置 timezone 参数。 比如匹配北京时间 "2018-06-18 11:10:00",则需要设置

date {    match => ["logdate", "yyyy-MM-dd HH:mm:ss"]    timezone => "Asia/Chongqing"}
1
  • 东八区
Standard Offset Canonical ID Aliases
+08:00 Asia/Chongqing Asia/Chungking
+08:00 Asia/Hong_Kong Hongkong
+08:00 Asia/Shanghai PRC

附: timezone 列表 (opens new window)

# 3,几个解析实战(重点)

# 1,标准格式ISO8601

如果日志的格式是如上说明的这种标准格式,那么这种日志出来是最省事儿的,不需要进行额外的解析,直接拿来使用即可,最常见的案例就是 NGINX 的日志了。

# 1,原始日志。

一般情况下,我们会把 NGINX 日志 json 化,关于时间戳的配置,都是这样:

"@timestamp":"$time_iso8601"
1

然后输出的日志如下:

{
  "@timestamp": "2019-10-12T21:57:40+08:00",
  "host": "172.16.241.250",
  "request_method": "GET",
  "clientip": "42.236.99.65",
  "size": 26977,
  "responsetime": 0.258,
  "upstreamtime": "0.258",
  "upstreamhost": "unix:/tmp/php-cgi.sock",
  "http_host": "wiki.eryajf.net",
  "url": "/index.php",
  "xff": "-",
  "referer": "http://wiki.eryajf.net/pages/2217.html",
  "agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.4.2661.102 Safari/537.36; 360Spider",
  "status": "200"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

那么这种日志,就不需要添加 data 插件进行解析,刨除其他特殊解析之外,直接进行输入输出即可,测试效果如下。

# 2,配置文件。

此时可用如下配置文件:

input {
	file {
		path 			=> ["/data/log/test.log"]
		codec 			=> "json"
	}
}

output {
	stdout {
		codec => rubydebug
	}
}
1
2
3
4
5
6
7
8
9
10
11
12

# 3,测试效果。

此时利用 rubydebug,查看控制台输出效果:

$ /usr/share/logstash/bin/logstash -f /usr/share/logstash/test.conf

。。。。。。
打印信息忽略
。。。。。。

{
           "referer" => "http://wiki.eryajf.net/pages/2217.html",
              "host" => "172.16.241.250",
             "agent" => "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.4.2661.102 Safari/537.36; 360Spider",
        "@timestamp" => 2019-10-12T13:57:40.000Z,
               "xff" => "-",
          "clientip" => "42.236.99.65",
               "url" => "/index.php",
      "upstreamtime" => "0.258",
              "path" => "/data/log/test.log",
              "size" => 26977,
         "http_host" => "wiki.eryajf.net",
          "@version" => "1",
            "status" => "200",
    "request_method" => "GET",
      "responsetime" => 0.258,
      "upstreamhost" => "unix:/tmp/php-cgi.sock"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

此时可以看到,我们没有针对原始日志进行任何解析操作,原始日志中的@timestamp字段就自动被 logstash 识别作为了日志的@timestamp,非常方便便捷,而且在这一层也少了解析的步骤,对 logstash 负载是大有好处的,因此我们鼓励开发者使用这种标准格式进行日志的输出打印,当这成为一个标准之后,一切就显得优雅了。

# 2,带 T模式。

我也不知道这种日志具体的格式名称,总之它的特征就是在时间戳里边有一个 T,这个T并不代表任何含义,仅仅作为一个时间戳隔离的无含义字段,因此我们在解析的时候,也可以将其无含义对待,直接套用即可。

# 1,原始日志。

这个一般是开发者定义时间格式,然后输出的日志如下:

{"timestamp":"2019-10-10T15:37:08+08:00","level":"info","message":"这是一条测试日志"}
1

套用上边表格里边的内容,直接引用后边的 match 配置即可。

# 2,配置文件。

此时可用如下配置文件:

input {
	file {
		path 			=> ["/data/log/test.log"]
		codec 			=> "json"
	}
}

filter {
	date {
		# 直接将T作为一个占位符,后边的时间也是,不过可以用Z代替
		match => [ "timestamp" , "yyyy-MM-dd'T'HH:mm:ss+08:00" ]
	}
}

output {
	stdout {
		codec => rubydebug
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# 3,测试效果。

此时利用 rubydebug,查看控制台输出效果:

$ /usr/share/logstash/bin/logstash -f /usr/share/logstash/test.conf

。。。。。。
打印信息忽略
。。。。。。

{
      "@version" => "1",
          "path" => "/data/log/test.log",
          "host" => "localhost.localdomain",
         "level" => "info",
     "timestamp" => "2019-10-10T15:37:08+08:00",
    "@timestamp" => 2019-10-10T07:37:08.000Z,
       "message" => "这是一条测试日志"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

生产当中,如果日志格式与上边例子一致,那么配置也是可以直接复用的,如果有出入,则应该根据实际情况进行特殊配置。

# 3,业务日志。

有一些应用的日志,或者业务的日志,时间格式极其不标准,各有各的想法,各有各的的模样,就像西游记里边长的各色各样的小妖一般,这种的处理起来就有点棘手了,需要借助于 grok 插件来进行处理。

# 1,原始日志。

这种就千奇百怪了,这里举一个例子,算是抛砖引玉,遇到一些相对特殊的,可以自行匹配转换,输出的日志如下:

[2019-09-17 17:35:30] ---.INFO: ===:{"code":0,"msg":"","nowTime":1565948130,"data":0}
1

# 2,配置文件。

此时可用如下配置文件:

input {
  file {
    path        => ["/data/log/test.log"]
    codec       => "json"
  }
}

filter {
  grok {
    match         => [ "message", "\[%{YEAR:year}-%{MONTHNUM:month}-%{MONTHDAY:day} %{TIME:time}\]%{GREEDYDATA}"]
    add_field     => { "timestamp" => "%{year}-%{month}-%{day} %{time}" }
    remove_field  => [ "day", "month", "year", "time"]
  }

  date {
    match         => [ "timestamp" , "dd/MMM/YYYY:HH:mm:ss Z", "UNIX", "yyyy-MM-dd HH:mm:ss", "dd-MMM-yyyy HH:mm:ss" ]
    target        => "@timestamp"
    remove_field  => "timestamp"
  }
}

output {
  stdout {
    codec => rubydebug
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

说明,这个地方,有必要用我个人理解到的,进行一下讲解,很多人都在分享着各种各样的配置,几乎从来没有人用通俗的话讲配置解析一下,而这样的事儿,恰恰又是我喜欢做的,因为这样在别人看到这里的时候,会更加容易有真实的收获。

  • grok

    将非结构化事件数据分析到字段中。 这个工具非常适用于系统日志,Apache 和其他网络服务器日志,MySQL 日志,以及通常为人类而不是计算机消耗的任何日志格式。

    • match

      这个概念不必介绍了,但是内部的意思值得说明一下,表示将整条日志放入到 message 这一字段中,其中,时间戳逐个匹配,匹配到的赋值给 "day", "month", "year", "time"。

    • add_field

      添加一个字段 timestamp,来承接刚刚解析的时间戳。即将"day", "month", "year", "time"赋值 g 诶 timestamp。

    • remove_field

      当解析到的字段已经赋值给了新的字段,那么原来解析到的"day", "month", "year", "time"就可以丢掉了。

  • date

    再往下就比较熟悉了,不必多介绍了,也是本文一直在讲解的内容。

# 3,测试效果。

此时利用 rubydebug,查看控制台输出效果:

$ /usr/share/logstash/bin/logstash -f /usr/share/logstash/test.conf

。。。。。。
打印信息忽略
。。。。。。

{
      "@version" => "1",
          "host" => "localhost.localdomain",
       "message" => "[2019-09-17 17:35:30] ---.INFO: ===:{\"code\":0,\"msg\":\"\",\"nowTime\":1565948130,\"data\":0}",
          "path" => "/data/log/test.log",
    "@timestamp" => 2019-09-17T09:35:30.000Z
}
1
2
3
4
5
6
7
8
9
10
11
12
13

这里的输出中,我们可以清晰看到,timestamp输出的时间,与原始日志中的时间是一致的,而且没有_dateparsefailure这个出现,说明解析规则是成功的。

# 4,系统日志 message。

有时候可能还需要采集系统的 message 日志,用于记录一些与系统相关的日志记录。

# 1,原始日志。

Linux 系统日志输出如下:

Oct 10 23:01:01 localhost systemd: Starting Session 9 of user root.
1

# 2,配置文件。

此时可用如下配置文件:

input {
	file {
		path 			=> ["/data/log/test.log"]
	}
}

filter {
	grok {
		match  => {"message" => "%{SYSLOGLINE}"}
	}
	date {
		match => ["timestamp","MMM dd HH:mm:ss"]
	}
	mutate {
		remove_field => ["timestamp"]
	}
}

output {
	stdout {
		codec => rubydebug
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 3,测试效果。

此时利用 rubydebug,查看控制台输出效果:

$ /usr/share/logstash/bin/logstash -f /usr/share/logstash/test.conf

。。。。。。
打印信息忽略
。。。。。。

{
       "message" => [
        [0] "Oct 10 23:01:01 localhost systemd: Starting Session 9 of user root.",
        [1] "Starting Session 9 of user root."
    ],
       "program" => "systemd",
          "path" => "/data/log/test.log",
    "@timestamp" => 2019-10-10T15:01:01.000Z,
          "host" => "localhost.localdomain",
      "@version" => "1",
     "logsource" => "localhost"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

这个配置,是借助于 elk 已经给出的一个插件 %{SYSLOGLINE}来完成,配置起来还是比较简便的。

好了,实际生产中实战的例子就举这么几个吧,常规的基本上都可用,如有其它比较特殊的,那么结合 grok 进行匹配即可。

# 4,时区问题

很多中国用户经常提一个问题:为什么 @timestamp 比我们早了 8 个小时?怎么修改成北京时间?

其实,Elasticsearch 内部(有相当多的软件,都是),对时间类型字段,是统一采用 UTC 时间,存成 long 长整形数据的!对日志统一采用 UTC 时间存储,是国际安全/运维界的一个通识——欧美公司的服务器普遍广泛分布在多个时区里——不像中国,地域横跨五个时区却只用北京时间。

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

所以,建议大家接受这种设定。否则,即便你用 .getLocalTime 修改,也还要面临在 Kibana 上反过去修改,以及 Elasticsearch 原有的 ["now-1h" TO "now"] 这种方便的搜索语句无法正常使用的尴尬。

微信 支付宝
#elk#logstash
上次更新: 2024/07/04, 22:40:37
logstash本地调试日志方法详解
logstash配置geoip画访问地域热图

← logstash本地调试日志方法详解 logstash配置geoip画访问地域热图→

最近更新
01
从赵心童世锦赛夺冠聊聊我的斯诺克情缘
05-16
02
学习周刊-总第211期-2025年第20周
05-15
03
记录二五年五一之短暂回归家庭
05-09
更多文章>
Theme by Vdoing | Copyright © 2017-2025 | 点击查看十年之约 | 浙ICP备18057030号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式