日志模版挖掘 - 工具篇
背景
日志是半结构化的数据,往往包含非常重要的信息。但是,对日志的应用通常局限在开发调试阶段。在运维阶段,也只是当系统出现问题后,需要救急时才来查阅。究其原因,是因为日志具有随意性,尤其是其根本目的是让人看懂,而不是让机器看懂。
其实,如果能让机器看懂日志,很多创新的应用场景就可以成为现实,比如失败预警、自动诊断、趋势分析等。
那么问题来了,如何用技术手段让机器看懂日志呢?一直以来,大家都依赖一种很古老的方法,那就是正则表达式。通过匹配日志中固定不变的字段,来识别这条日志。包括最近很流行的Logstash,虽然可以使用更时髦的Grok, 也仍然需要通过人工编制这样的表达式来实现日志的识别。
这本质还是需要人能看懂。 例如,当管理员看到:
2014-04-16 00:25:58.253 15432 INFO nova.osapi_compute.wsgi.server [req-f8a88e2d-c8fc-425f-9c8d-fc626219e2f3 d38af5fbcd0b4cea96fc024f1e2eb317 2208a2cc93da4ccab0af4ebda9c0f745] 9.186.106.52 "GET /v2/2208a2cc93da4ccab0af4ebda9c0f745/images/187dd0ec-daa8-4d9e-abf4-f7bce9c1800c HTTP/1.1" status: 200 len: 891 time: 0.0819170
他需要编写出如下的Grok来对其进行匹配:
%{TIMESTAMP_ISO8601:timestamp}%{SPACE}%{WORD:pid}%{SPACE}%{WORD:loglevel}%{SPACE}%{GREEDYDATA:issuer}%{SYSLOG5424SD:requestid}%{SPACE}%{IP:ip}%{SPACE}%{QS:request}%{SPACE}status:%{SPACE}%{NUMBER:response}%{SPACE}len:%{SPACE}%{NUMBER:length:int}%{SPACE}time:%{SPACE}%{NUMBER:time:float}
这无疑需要极大的耐心。而且实际系统中的日志又是千奇百怪,不可能针对每一种出现的日志都人工进行人工编制。因此,在我们看到的日志归集系统中,真正被有效处理的日志与信息少之又少。
工具
为了解决这个问题,我开发了一个工具,名叫LogTemplate。顾名思义,它能够自动挖掘日志中的模版(template)。在这里我们先简单介绍下这个工具的使用,它的实现原理我会在后续文章中介绍。
LogTemplate基于Python开发,可以在安装了Python的机器上直接运行。Git clone后进入LogTemplate文件夹,README.md中给出了使用方法。在这里,我们着重介绍下工具需要的输入参数。
- -f: 也就是filepath,需要分析的日志文件路径。
- -s: 也就是support,是一个正整形参数,可以调整所挖掘模版的质量与数量。数值越大,模版质量越好,但挖掘到的数量越少。 通常情况下,我们需要预估下日志文件的样本规模。假设一个模版能够匹配 N 个样本,那么该参数可以设定为 N/2。
- -p : 也就是peeler,剥皮器,是一个简单的配置,告诉工具去分析日志中的哪些部分,以及该怎么分析。
这里重点讨论下peeler。在sample文件夹中有一组典型的访问日志,peeler文件夹中是与其对应的剥皮器。
下面是日志中的一行:
64.242.88.10 - - [07/Mar/2004:16:05:49 -0800] "GET /twiki/bin/edit/Main/Double_bounce_sender?topicparent=Main.ConfigurationVariables HTTP/1.1" 401 12846
所谓日志是半结构化的,是指它有时间戳、IP地址、级别等固定不变(结构化)的部分,以及正文(message)这个变化(非结构化)的部分。 日志模版的提取主要针对非结构化的部分,因为结构化的部分可以通过字段位置很容易的识别。
那么,对应这样日志文件的剥皮器是这样的:
{"outer_delimiter":" ","inner_delimiter":" |/|\"","message_start":"5","message_end":"-2"}
这是一个一行的json,里面包括四个字段:
- outer_delimiter, message_start 和 message_end 告诉工具正文所在的位置。通常情况下,outer_delimiter设置为空格(” “)即可,message_start和message_end是message在以outer_delimiter分隔后的行中的位置。由于日志行中结构化的部分位置是固定的,所以message会有一个同样固定的起始位置。对于其结束位置,我们采用类似Python List的表示方法,使用一个负整数或空字符来表示其与行尾的相对位置。例如这里的“-2”就表示message在倒数第二个空格结束。如果message直到行尾,则用一个空字符串(“”)表示。
- inner_delimiter 告诉工具如何分解message。对于普通的日志,设置成为空格就可以。在这个例子中,我们想分析URL类型,所以除法符号()与冒号(“)也被加入进来。 多种符号可以用 | 表示组合,与正则表达式的规范相同。
有了这些参数的设置,我们的工具就可以智能的挖掘日志模版了。sample文件夹中有support设置为50时对示例日志所挖掘的模版。您也可以使用这个工具方便的处理自己的日志了。
工具的GitHub链接:https://github.com/cameling/logminer/tree/master/LogTemplate