日志分析方法概述

日志在计算机系统中是一个很是普遍的概念,任何程序都有可能输出日志:操做系统内核、各类应用服务器等等。日志的内容、规模和用途也各不相同,很难一律而论。前端

本文讨论的日志处理方法中的日志,仅指Web日志。其实并无精确的定义,可能包括但不限于各类前端Web服务器——apache、lighttpd、tomcat等产生的用户访问日志,以及各类Web应用程序本身输出的日志。正则表达式

在Web日志中,每条日志一般表明着用户的一次访问行为,例以下面就是一条典型的apache日志:算法

211.87.152.44 – - [18/Mar/2005:12:21:42 +0800] “GET / HTTP/1.1″ 200 899 “http://www.baidu.com/” “Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon)”数据库

从上面这条日志中,咱们能够获得不少有用的信息,例如访问者的IP、访问的时间、访问的目标网页、来源的地址以及访问者所使用的客户端的UserAgent信息等。若是须要更多的信息,则要用其它手段去获取:例如想获得用户屏幕的分辨率,通常须要使用js代码单独发送请求;而若是想获得诸如用户访问的具体新闻标题等信息,则可能须要Web应用程序在本身的代码里输出。apache

为何要分析日志

毫无疑问,Web日志中包含了大量人们——主要是产品分析人员会感兴趣的信息,最简单的,咱们能够从中获取网站每类页面的PV值(PageView,页面访问量)、独立IP数(即去重以后的IP数量)等;稍微复杂一些的,能够计算得出用户所检索的关键词排行榜、用户停留时间最高的页面等;更复杂的,构建广告点击模型、分析用户行为特征等等。编程

既然这些数据是如此的有用,那么固然已经有无数现成的工具能够帮助咱们来分析它们,例如awstats、Webalizer,都是专门用于统计分析Web服务器日志的免费程序。tomcat

另外还有一类产品,它们不分析直接日志,而是经过让用户在页面中嵌入js代码的方式来直接进行数据统计,或者说咱们能够认为它是直接让日志输出到了它们的服务器。典型的表明产品——大名鼎鼎的Google Analytics,另外还有国内的cnzz、百度统计等。服务器

不少人可能会说,既然如此,咱们为何还须要本身来分析日志,有必要吗?固然有。咱们的用户(产品分析人员)需求是无穷尽的,上面说的这几类工具虽然很好很强大,但显然没办法知足所有的需求。并发

不管是本地分析的工具,仍是在线的分析服务,它们虽然提很丰富的的统计分析功能,能够作必定程度的配置,可是依然颇有限的。要进行稍复杂点的分析,或者要作基于日志的数据挖掘,依然须要本身来完成。app

另外绝大多很多天志分析工具都是只能用于单机的,数据量稍大就没辙了。同时那些提供在线分析的服务对于单个站点一般也都有最大流量的限制——这是很容易理解的,他们也须要考虑服务器的负载。

因此,不少时候仍是得靠本身。

怎么进行日志分析

这并非一个简单的问题。即便咱们把“日志”限定为Web日志,依然包含了成千上万种可能的格式和数据,而是“分析”更是难以定义,也许是简单的统计值的计算,也许是复杂的数据挖掘算法。

下面并不打算讨论这些复杂的问题,而只是笼统的讨论如何构建进行日志分析工做的基础。有了这些基础会让基于日志的简单统计分析变得很简单,并让复杂的分析挖掘等变得可行。

少许数据的状况

先考虑最简单的状况,在数据规模比较小的时候,也许是几十MB、几百MB或者几十GB,总之就是在单机处理尚能忍受的时候。一切都很好办,现成的各类Unix/Linux工具——awk、grep、sort、join等都是日志分析的利器,若是仅仅是想知道某个页面的PV,一个wc+grep就能搞定。若是有稍复杂的逻辑,那就使用各类脚本语言,尤为是perl,配合伟大的正则表达式,基本就能够解决全部的问题。

例如,咱们想从上面提到的apache日志中获得访问量最高前100个IP,实现很简单:

cat logfile | awk ‘{a[$1]++} END {for(b in a) print b”\t”a[b]}’|sort -k2 -r|head -n 100

不过当咱们须要频繁去分析日志的时候,上面的作法在一段时间以后可能就会让咱们头疼如何进行各类日志文件、用于分析的脚本文件、crontab文件等等的维护,而且可能会存在大量重复的代码来作数据格式的解析和清洗,这个时候也许就须要更合适的东西,好比——数据库。

固然,要使用数据库来进行日志分析仍是须要一些代价的,最主要的就是如何将各类异构的日志文件导入的数据库中——这个过程一般称为ETL(Extraction-Transformation-Loading)。幸亏依然有各类现成的开源、免费的工具来帮助咱们作这件事情,而且在日志种类不太多的时候,本身写几个简单的脚原本完成这项工做也并不困难。例如能够将上面的日志去掉没必要要的字段,而后导入以下的数据库中:

 

如今须要考虑一下用什么数据库来存储这些数据。MySQL是一个很经典的开源数据库,它的传统引擎(MyISAM或者InnoDB,行存储)也许并不很是的适合日志数据的存储,可是在小数据量的时候仍是很够用的。并且,在这方面如今已经有了更好的选择,例如开源且免费的Infobright、Infinidb,都是专门为数据仓库应用而进行了优化的数据引擎,采用列存储,有良好的数据压缩,处理几百GB的数据基本上不是问题。

使用数据库的好处之一就是,伟大的SQL能够帮咱们很简单的完成绝大部分的统计分析工做——PV只须要SELECT+COUNT,计算搜索词排行只须要SELECT+COUNT+GROUP+ORDER+LIMIT。此外,数据库自己的结构化存储模式也让日志数据的管理变的更简单,减小运维代价。

一样仍是上面的那个例子,简单的一个SQL就能够搞定:

SELECT * FROM (SELECT ip, COUNT(*) AS ip_count FROM apache_log GROUP BY ip) a ORDER BY ip_count DESC LIMIT 100

至于性能问题,数据库的索引和各类优化机制一般会让咱们的统计分析工做变得更快,而且上面提到的Infobright和Infinidb都专门为相似SUM、COUNt之类的汇集应用作了优化。固然也不是绝对的会快,例如在数据库中进行LIKE操做,一般会比grep一个文件还要慢不少。

更进一步的,使用基于数据库的存储,能够很容易的进行OLAP(联机分析处理)应用,从日志中挖掘价值会变的更加简单。

更多的数据怎么办

一个好的数据库彷佛会让事情变的很简单,可是别忘了前面提到的都是单机数据库。一台单机在存储容量、并发性上毫无疑问都是有很大限制的。而日志数据的特色之一就是随时间持续增加,而且因为不少分析过程每每须要历史数据。短期内的增加也许能够经过分库、分表或者数据压缩等来解决,不过很显然并非长久之计。

想要完全解决数据规模增加带来的问题,很天然的会想到使用分布式技术,结合上面的结论,也许使用某个分布式数据库是一个好选择,那么对最终用户就能够彻底透明了。这个的确是很理想的状况,不过现实每每是残酷的。

首先,实现比较完美的分布式数据库(受限于CAP原则)是一个很是复杂的问题,所以在这里并不像单机数据库那样,有那么多开源的好东西能够用,甚至于商用的也并非太多。固然,也并不是绝对,若是有钱,仍是能够考虑一下Oracle RAC、Greenplum之类东西。

其次,绝大多数分布式数据库都是NoSQL的,因此想继续用上SQL的那些优势基本上是没期望,取而代之的都是一些简单、难以使用的接口。单从这点看来,使用这些数据库的价值已经下降不少了。

因此,仍是先现实一点,先退一步考虑如何解决的超大规模的日志的分析问题,而不是想如何让它变的像在小数据规模时那样简单。单单想作到这点,目前看来并非太难,而且依然有免费的午饭能够吃。

Hadoop是伟大的Apache基金会下面的一套分布式系统,包括分布式文件系统(HDFS)、MapReduce计算框架、HBase等不少组件——这些基本都是Google的GFS/MapReduce/BigTable的克隆产品。

Hadoop通过数年的发展,目前已经很成熟了,尤为是其中的HDFS和MapReduce计算框架组件。数百台机器的集群已经被证实可使用,能够承担PB级别的数据。

Hadoop项目中的HBase是一个按列存储的NoSQL分布式数据库,它提供的功能和接口都很是简单,只能进行简单的K-V查询,所以并不直接适用于大多很多天志分析应用。因此通常使用Hadoop来作日志分析,首先仍是须要将日志存储在HDFS中,而后再使用它提供的MapReduce API编写日志分析程序。

MapReduce是一种分布式编程模型,并不难学习,可是很显然使用它来处理日志的代价依然远大于单机脚本或者SQL。一个简单的词频统计计算可能都须要上百代码——SQL只须要一行,另外还有复杂的环境准备和启动脚本。

例如一样仍是上面的例子,实现就要复杂的多,一般须要两轮MapReduce来完成。首先要在第一轮的mapper中计算部分ip的访问次数之和,并以ip为key输出:

//遍历输入,并聚合结果

foreach(record in input) {

ip = record.ip;

dict[ip]++;

}

//用emit输出,第一个参数为key,用于reduce的分发

foreach(<ip, count> in dict) {

emit(ip, count);

}

而后在第一轮的reduce中就能够获得每一个ip完整的计数,能够顺便排个序,而且只保留前100个。

count = 0;

//对于每一个key(ip),遍历全部的values(count),并累加

while(input.values.hasNext()) {

count += input.values.next();

}

//插入到大小为100的堆中

heap_insert(input.key, count);

在reduce结束的时候输出:

//输出当前reduce中count最高的100个ip

foreach(<ip, count> in dict) {

emit(ip, count);

}

因为reduce通常会有不少个,因此最后还须要将全部reduce的输出进行合并、再排序,并获得最终的前100个IP以及对应的访问量。

因此,使用Hadoop来作日志分析很显然不是一件简单事情,它带来了不少的额外的学习和运维成本,可是至少,它让超大规模的日志分析变成了可能。

怎样变得更简单

在超大规模的数据上作任何事情都不是一件容易的事情,包括日志分析,但也并非说分布式的日志分析就必定要去写MapReduce代码,老是能够去作进一步的抽象,在特定的应用下让事情变得更简单。

也许有人会很天然的想到若是能用SQL来操做Hadoop上的数据该有多好。事实上,不只仅只有你一我的会这么想,不少人都这么想,而且他们实现了这个想法,因而就有了Hive。

Hive如今也是Hadoop项目下面的一个子项目,它可让咱们用SQL的接口来执行MapReduce,甚至提供了JDBC和ODBC的接口。有了这个以后,Hadoop基本上被包装成一个数据库。固然实际上Hive的SQL最终仍是被翻译成了MapReduce代码来执行,所以即便最简单的SQL可能也要执行好几十秒。幸亏在一般的离线日志分析中,这个时间仍是能够接受的。更重要的是,对于上面提到的例子,咱们又能够用同样的SQL来完成分析任务了。

固然Hive并非彻底的兼容SQL语法,并且也不能作到彻底的对用户屏蔽细节。不少时候为了执行性能的优化,依然须要用户去了解一些MapReduce的基本知识,根据本身的应用模式来设置一些参数,不然咱们可能会发现一个查询执行很慢,或者压根执行不出来。

另外,很显然Hive也并不能覆盖全部的需求,因此它依然保留插入原始MapReduce代码的接口,以便扩展。

更多的问题

即便有了Hive这样一个相似于数据库的东西,咱们依然还有不少事情须要作。例如时间久了,可能会有愈来愈多的须要例行执行的SQL,而这些SQL中,也许有一些是作了重复的事情;也许有一些的执行效率很是低下,一个复杂的SQL就占满了全部的计算资源。这样的系统会变得愈来愈难以维护的,直到有一天例行的SQL终于跑不完了。而最终用户每每不会去关心这些事情,他们只关心本身提交的查询是否是能即时获得响应,怎么样才能尽快的拿到结果。

举个简单的例子,若是发如今使用apache_log的全部查询中,几乎没有人用其中的user_agent字段,那么咱们彻底能够把这个字段去除掉,或者拆分红两张表,以减小多数查询的IO时间,提升执行的效率。

为了系统化的解决这些问题,咱们可能须要引入例行任务的调度机制,可能须要去分析全部的SQL来发现哪些是能够合并的、哪些的性能须要优化,使用的数据表是否是须要作水平或者垂直分表等等。根据实际状况的不一样,这时事情多是人工来完成,也多是写程序来自动分析并调整。

再者随着日志类型、分析需求的不断增加。用户会愈来愈多的抱怨很难找到想要的数据在哪份日志里,或者跑的好好的查询由于日志格式的变化而忽然不能用了。另外上面提到的ETL过程也会变得复杂,简单的转换导入脚本极可能已经解决不了问题。这时候可能须要构建一个数据管理系统,或者干脆考虑创建一个所谓的数据仓库。

总之,随着日志数据量、日志类型、用户数量、分析需求等等的不断增加,愈来愈多的问题会逐渐浮现出来,日志分析这件事情可能就再也不像咱们最初想的那么简单,会变得愈来愈有价值,也愈来愈有挑战。

相关文章
相关标签/搜索