2.4 系统日志的数据形式
系统日志和事件的数据存储形式主要有两类。一类是无结构的日志数据,例如日志文件,常见的Linux日志、Apache服务器日志、Hadoop 日志等的日志数据都记录在一个纯文本日志文件中。每条日志或者事件都以一条文本消息或者短文的形式存储在日志文件内。另外一类是结构化或者半结构化的日志事件数据,比如Windows Event Logs、数据库历史查询记录日志以及IBM Tivoli Monitoring[17]监测事件等。每条数据库记录代表一个日志或者事件。每条记录会将该日志事件的各个属性分开存储到表的各个字段内。常见的字段包括日志产生的时间、机器名、进程名、错误代码、异常详细描述等。
2.4.1 无结构的日志数据
图2-3是一个Hadoop平台下DataNode的日志文件。
图2-3 Hadoop DataNode日志
每条日志大概分3个部分:日志产生的日期和时间;日志产生的Java类;消息。消息部分的内容是Hadoop开发人员定义在Hadoop源代码内部的。消息部分描述了当前程序在执行何种任务或者遇到何种错误异常。这些日志数据都是在标准 Java logging工具(例如apache common logs、log4j等)下生成的。例如,第一条记录表示在2011-01-26 10:38:49这个时刻,该Hadoop的DataNode类在执行STARTUP (启动)这个事件。第二条记录则是表示一个错误异常信息:NullPointerException(空指针异常)。第三条记录则是这个DataNode类主动发起SHUTDOWN_MSG(关闭)的时间。直到第 7 条记录,该 DataNode 得以成功启动。整体来说,每条日志记录都是描述一个系统事件,且可以由一个标准事件的三元组构成(时间、地点、行为)。正因如此,本书将系统事件和系统日志看作同一个概念。
很多应用程序并没有标准的日志格式。诸如一些 UNIX/Linux 下开发的应用软件可能使用其他的日志生成代码。甚至在同一个软件系统内,不同模块生成的日志格式也完全不一样,因为不同模块的开发可能是由不同开发团队或者公司完成的,且团队和公司之前并没有统一格式规范。另外,由于日志功能需求上的不同,不同软件系统生成的日志格式也可能完全不一样。例如当开发人员十分注重一个软件的数据吞吐量的时候,他们会在日志数据中嵌入大量衡量数据吞吐量的标准。当开发人员更注重一个软件的业务逻辑和执行步骤的时候,他会更多地在日志数据中嵌入业务逻辑的描述。总的来说,系统日志数据千差万别。在处理一个具体系统的日志时,分析方法可能也会千差万别。一个很现实的问题就是:大量开源软件和商业软件没有详尽的关于日志数据的文档说明。这对于人工和自动的日志分析都是一大挑战。很多日志分析的工作都依赖于系统管理员在此领域的个人经验。在后面的章节里,我们也会针对此问题介绍一些基于数据挖掘算法分析此类日志数据的方法。
2.4.2 结构化与半结构化的日志数据
最常见的半结构化日志数据就是Windows Event Logs。图2-4是一个Windows 7的Event Viewer应用程序的截图。Event Viewer是Windows操作系统内查看Windows Event Logs数据的图形界面程序。Windows系统大致把其Event Logs分成五大类:Application、Security、Setup、System、Forwarded Events。Application是Windows操作系统内的应用程序生成的日志数据。Security 是与安全相关的事件,比如Windows自身的防火墙会阻挡某些外部TCP请求,这些被阻挡的TCP请求就会被记录在这部分日志数据内。Setup主要是应用程序的安装和Windows组件的安装、卸载以及修改等事件,比如最典型的就是Windows Update补丁的安装记录。System是系统内部运行状态的各种日志数据,比如某个服务被停止、某个服务进程被开启等。Forwarded Events是从其他计算机转发的信息。
图2-4 Windows Event Viewer
图2-5是一个原始XML的Security事件日志。通过XML的各个标签可以很清楚地看到这个事件发生的事件名、机器名、用户名、域名、进程名以及创建进程的执行文件路径。图2-5中的机器名、域名和用户名被xxx取代。此类日志数据可以通过常见的 XML 解析软件得到各个关键的事件属性,这对于自动化的日志数据分析来说是一大便利之处。
图2-5 XML形式的Windows Event Logs
除了 Windows Event Logs 之外,很多系统监控系统(比如 IBM Tivoli Monitoring[20]、HP OpenView[21])生成的日志事件数据也是以半结构化的形式存储在关系数据库内。不同之处在于,专业监控系统可以通过管理员定制各种复杂的监控信息,产生各种不同的日志数据。以IBM Tivoli Monitoring为例,它可以让监控管理员监控硬件设备、操作系统内核,甚至Web内部消息的所有信息。这些日志数据并不保存在服务器自身的文件系统中,而是直接写入海量数据中心的关系数据库或者NoSQL数据库[22]。系统管理员可以通过SQL语句查询其关注的某些日志事件。有效管理海量系统日志属于数据库和数据挖掘领域的一个交织应用领域。在工业界,除了 IBM、HP 等大企业之外,也有很多专门的企业和商业软件提出各种不同的体系架构和解决方案,如Splunk[18]、AppFirst[19]。
2.4.3 非结构化数据的转换
很多数据挖掘的分析算法都建立在结构化的事件上,但是有很多日志数据是半结构或者无结构化的文本。在进行分析之前,我们需要将这种文本数据转换成为结构化的事件[23]。在传统的自然语言处理领域,这个任务就是典型的信息抽取。信息抽取有基于规则的,也有基于统计模型的。基于规则的方法比较简单,也比较实用。在学术界,大家研究更多的是基于统计模型的,例如Conditional Random Field[24]等。对于系统日志数据来说,格式和变化相对于人类语言其实很小,无论是用基于规则还是基于Conditional Random Field的方法都完全可以取得很高的精确度,而且比一般自然语言处理的文本的处理结果更可靠。
在没有训练数据的情况下,无结构的日志数据转换还可以通过分析其日志生成的源代码完成[25]。现在很多软件都有专门的日志生成工具包,例如用java.util.logging和Apache Log4J[26]等标准库来规范日志。换句话说,这些日志生成源代码并没有多大的变化,通过源代码文件中的字符串匹配就可以找到生成日志的函数代码。然后再对其进行类似形式语言的语法处理,就可以抽取出日志里的字符串和变量。
很多商业软件系统的源代码并不是可以获取的。即便可以获取,也不一定是完整的,因为一个软件系统可能用了无数第三方的软件库或者工具包。再次,大型软件系统的源代码太庞大,除了软件供应开发商之外,由其他任何机构来整理和分析都是一个浩大的工程。参考文献[27]提出一种基于聚类算法将文本日志转换成为各种不同类型事件的方法。其中,每条日志文本的距离通过简单的逐词匹配获取。两条文本日志在每个位置上互相匹配的词越多,就认为这两条文本日志的距离越小。参考文献[28]提出的是一种基于层次的聚类算法。每层抽取不同的日志文本格式信息进行聚类。这两类方法对日志文本的格式一致性要求太高。如果两个日志文本长度不一样,或者说有些细节变化,都将导致日志文本完全归为不同类别。参考文献[29]针对文本日志提出一种基于短语标签(Signature)的方式进行聚类。这里的短语标签通常是代表一类日志事件特色的短语结构,如“database tablespace”“paging switch”。日志文本通常很短,一旦出现这类短语,就能足够准确地将该日志文本进行分类。短语标签的提出思想来自于最长公共子串(Multiple Longest Common Subsequence)问题[30]。需要注意的是,在一般算法教程里介绍动态规划的时候,都是用2个串来寻找最长公共子串的。这里的Multiple Longest Common Subsequence是一个扩展问题,假设我们有n个串,那么最长的共同子串是什么?公共子串不要求一定由连续的词组成,所以它的顽健性比前面的方法都强。但是寻找n个串的最长公共子串是一个著名的NP难问题[29]。基于短语标签的聚类算法的目标就是将所有的日志文本集分成k个簇,并从每个簇找出一个短语标签,然后试图让簇内的日志文本尽量和这个短语标签匹配。与最长公共子串问题不同的是,这里并不要求短语标签被所有的簇内日志文本所包含,而是计算一个匹配度的量化指标。但是这个问题依然可以被证明和n个串的最长公共子串问题的难度是等价的。具体的近似算法见参考文献[29]。