基于Java的日志分析工具——LogAnalyzerFramework,小轮子一个

分析日志并不是Java擅长的工作,多数时候我们经常会把这项工作交给shell\Perl\Python之类的脚本去处理。但是很多时候因为所处的“大环境”是Java,在分析日志的时候需要去调用一些Java的接口,那么日志分析工具就得用Java来写了。分析日志是个很繁琐的工作,需要每次都去打开文件、一行行处理然后关闭文件,如果是动态日志分析还需要去维护文件指针。于是这种工作做多了烦了之后就把这些繁琐的通用逻辑抽出来,做成框架的形式,这样在每次编写日志分析程序的时候只需要去关注业务逻辑,而不必去关心指针或者IO之类的琐事。

小项目,没必要起个很文艺的名,其实更重要的是我也不文艺,干脆就叫LogAnalyzerFramework,项目地址:https://github.com/chihongze/LogAnalyzerFramework 下面来简单谈下这东西的设计。

对于一个日志文件的分析做为一个"task",一个task可以包含多个analyzelet,比如我们要分析web服务的日志,既要分析出其中的异常,又要分析用户行为,那么就可以把这两个业务编写成两个不同的analyzelet,这样在遍历一次日志的时候会同时执行多个业务。可以理解为一个事件触发多个监听器的模式。定义task不需要编写任何Java代码,只需要编写一个简单的yaml就可以了,比如:

taskName: testTask
pointerName: /data/test_ptr
cmdOptions: [a,b,c,d]
initParams: {test: true}
resetPtrFileTime: 30
logFileName: /data/web_log/logs/www_stdout.log
analyzelets: [loganalyzer.core.test.TestAnalyzelet]

taskName:定义任务的名称,没什么大作用,只是在log中做个标识

pointerName:记录文件指针的文件路径,如果分析的是动态增长的log,那么就需要记录上次分析到什么地方,以便下次访问的时候不会重复访问。如果指定这个选项,那么就会按文件中记录的指针来访问文件,如果不指定,那么就每次都是从头开始访问文件。

cmdOptions:接受的命令行参数名称,通过从命令行指定的参数,都会被封装到loganalyzer.core.AnalyzerContext对象当中。可以在Analyzelet中获取它们。

initParams:初始化配置参数,也同样会写到loganalyzer.core.AnalyerContext对象中

resetPtrFileTime:指针文件重置时间。很多时候为了防止一个日志文件过大,会采取通过时间来切割的方式,对于切割的日志文件,指针文件也需要跟随更新,这里指定指针文件的更新周期,时间单位是分钟。

logFileName:要分析的日志文件名。但是很多时候我们要分析的日志文件名需要去动态生成,比如今天要去分析昨天切割的日志,这里的文件名就不能写死,怎么办呢?提供有另一个选项:logFileNameGenerator,只要写一个类实现loganalyzer.core.LogFileNameGenerator,定义文件名的动态生成逻辑就可以了。然后把这个类的全限定名作为值指定给logFileNameGenerator属性即可。嗯,其实同Spring的BeanNameGenerator一样。

analyzelets:这里指定该task要附加运行的analzyelets,日志分析的所有业务逻辑都包含在analyzlet中。

下面来看一下如何编写一个Analyzelet,很简单,只需要实现loganalyzer.core.Analyzelet接口就可,该接口的定义如下:

package loganalyzer.core;

/**
 * 定义一个日志分析逻辑的声明周期
 * @author  [email protected]
 *
 */
public interface Analyzelet {

	/**
	 * 分析之前要处理的事务,相当于awk的begin操作块
	 * @param analyzerContext
	 */
	public void begin(AnalyzerContext analyzerContext);
	
	/**
	 * 处理每一行log的逻辑
	 * @param line
	 * @param analyzerContext
	 */
	public void doLine(String line, AnalyzerContext analyzerContext);
	
	/**
	 * 最终结束的逻辑,相当于awk的end操作,注意,如果分析中途发生错误,那么不会执行此方法,而是执行onError方法。
	 * @param analyzerContext
	 */
	public void end(AnalyzerContext analyzerContext);
	
	/**
	 * 错误处理逻辑
	 * @param analyzerContext
	 */
	public void onError(AnalyzerContext analyzerContext, Throwable t);
	
	/**
	 * 遇到错误是否立即停止
	 * @return
	 */
	public boolean onErrorStop();
}

其实,可以把它想像成shell下awk的几个过程,begin同awk的BEGIN,定义在分析之前要做得事情,比如你要在日志分析的过程中累加一个数值,那么就可以在begin中把它定义到AnalyzerContext里面,AnalyzerContext存储key-value对。然后在分析的时候,在doLine中将这个值累加,在end的时候把这个值做最终处理,比如发个邮件报表之类的。另外比awk多的过程就是onError,定义了出错时的处理逻辑,还有一个项是onErrorStop,如果这个返回的是true,那么一旦在处理的过程中发生错误,那么就会立即停止Analyzelet的处理,否则会忽略这条错误,继续处理下去,需要跟据自己的业务情况来指定返回值。

多数时候我们只需要关注doLine就可以了,那么继承loganalyzer.core.BasicAnalyzelet,实现doLine,其余的方法如果不重写的话,只会输出一行简单的log。

编写完analyzelet和task,就可以进行日志分析了,如何启动呢?

1.从命令行环境启动:

专门有一个从命令行启动的入口:loganalyzer.core.AnalyseLogTaskLauncher

java -cp ./lib/ loganalyzer.core.AnalyseLogTaskLauncher -log ./log4j.xml -task /data/tasks/test_task.yaml

有两个命令行参数,-log参数指定log4j配置文件的位置,-task指定要运行的task配置,后面可以跟task配置中cmdOptions所指定的项。

 

2. 在Java代码中调用:

也可以在其它的Java程序中启动一个日志分析任务:

AnalyzerLogTaskExecutor executor = BasicAnalyzerLogTaskExecutor.getInstance();
AnalyzeTaskConfig taskConfig = AnalyzeTaskConfig.loadFromConfigFile("/data/test_task.yaml");
AnalyzerContext context = new AnalyzerContext();
context.addParam(taskConfig.getInitParams());
executor.execute(taskConfig, context);

AnalyzerLogTaskExecutor是一个封装日志分析过程的门面,把需要的参数传给它,执行execute方法,就可以完成日志分析任务。

你可能感兴趣的:(基于Java的日志分析工具——LogAnalyzerFramework,小轮子一个)