2.2日志记录部分的设计
如前文所述,日志记录部分负责接收日志系统客户端发送来的日志消息、日志对象的管理等工作。下面详细描述了日志记录部分的设计要点:
1.日志记录器的管理
系统通过保持多个Logger对象的方式来进行日志记录的分类。每一个Logger对象代表一类日志分类。因此,Logger对象的名称属性是其唯一标识,通过名称属性获取一个Logger对象:
1.LoggerLoggerlogger=Logger.getLogger(“LoggerName”);
一般的,使用类名来作为日志记录器的名称,这样做的好处在于能够尽量减少日志记录器命名之间的冲突(因为Java类使用包名),同时能够将日志记录分类得尽可能的精细。因此,假定有一UserManager类需要使用日志服务,则更一般的使用方式为:
2.LoggerLoggerlogger=Logger.getLogger(UserManager.class);
2.日志分级的实现
按照日志目的不同,将日志的级别由低到高分成五个级别:
◆DEBUG-表示输出的日志为一个调试信息;
◆INFO-表示输出的日志是一个系统提示;
◆WARN-表示输出的日志是一个警告信息;
◆ERROR-表示输出的日志是一个系统错误;
◆FATAL-表示输出的日志是一个导致系统崩溃严重错误。
这些日志级别定义在LoggerLevel接口中,被日志记录器Logger在内部使用。而对于日志系统客户端则可使用Logger类接口对直接调用并输出这些级别的日志,Logger的这些接口描述如下:
3.publicvoiddebug(Stringmsg);//输出调试信息
4.publicvoidinfo(Stringmsg);//输出系统提示
5.publicvoidwarn(Stringmsg);//输出警告信息
6.publicvoidfatal(Stringmsg);//输出系统错误
7.publicvoiderror(Stringmsg);//输出严重错误
通过对Logger对象上这些接口的调用,直接为日志信息赋予了级别属性,这样为后继的按照不同级别进行输出的工作奠定了基础。
3.日志对象信息的获取
日志对象上包含了日志所具备的所有信息。通常这些信息包括:输出日志的时间、Java类、类成员方法、所在行号、日志体、日志级别等等。在JDK1.4中可以通过在方法中抛出并且捕获住一个异常,则在捕捉到的异常对象中已经由JVM自动填充好了系统调用的堆栈,在JDK1.4中则可以使用java.lang.StackTraceElement获取到每一个堆栈项的基本信息,通过对日志客户端输出日志方法调用层数的推算,则可以比较容易的获取到StackTraceElement对象,从而获取到输出日志时的Java类、类成员方法、所在行号等信息。在JDK1.3或者更早的版本中,相应的工作则必须通过将异常的堆栈信息输出到字符串中,并分析该字符串格式得到。
2.3日志输出部分的设计
日志输出部分的设计具有一定的难度,在本文设计的日志系统中,日志的输出、多线程的支持、日志系统的扩展性、日志系统的效率等问题都交由日志输出部分进行管理。
1.日志输出器的继承结构
在日志的输出部分采用了二层结构,即定义了一个抽象的日志输出器(AbstractLoggerAppender),然后从该抽象类继承出实际的日志输出器。AbstractLoggerAppender定义了一系列的对日志进行过滤的方法,而具体输出到媒介的方法则是一个抽象方法,由子类实现。在系统中默认实现了控制台输出器和文件输出器两种,其中控制台输出器的实现颇为简单。
2.文件输出器的内部实现
在日志记录部分的实现中,并没有考虑多线程、高效率等问题,因此文件输出器必须考虑这些问题的处理。在文件输出器内部使用java.lang.Vector定义了一个线程安全的高速缓冲,所有通过日志记录部分分派到文件输出器的日志被直接放置到该高速缓冲当中。同时在文件输出器内部定义一个工作线程,负责定期将高速缓冲中的内容保存到文件,在保存的过程中同时可以进行日志文件的备份等工作。由于采用了高速缓冲的结构,很显然日志客户端的调用已经不再是一个同步调用,从而不再会需要等到文件操作后才返回,提高的系统调用的速度。该原理如图3所示:
2.4设计难点
通过上述设计,一个具有良好扩展能力的高性能日志系统框架就已经具有了一定的雏形。在设计过程中几个难点问题需要进一步反思。
一、是否整个系统应当采用完全异步的结构,通过类似于消息机制的方式来进行由日志客户端发送日志给日志系统。这种方式可以作为日志系统框架另一种运行方式,在后继设计中加以考虑。
二、在文件输出器中可以看到,目前虽然可以扩展多个日志输出器,但是目前提供的抽象类中仅仅提供了对日志的过滤机制,而没有提供的缓存机制,目前的缓存机制被放在文件输出器中实现,因此在未来的进一步设计中,可以将文件输出器中的缓存机制上移到抽象类当中。
2.5设计模式
在设计过程中我们特别注意使用了数个经典的设计模式。如:Logger对象的创建使用了工厂方法模式(FactoryMethod)、由AbstractLoggerAppender和ConsoleAppender以及FileAppender构成了策略模式(Strategy),除此以外,还大量使用了单例模式(Singleton)。在设计中适当运用设计模式能够加快设计进度、提高设计质量。
3.总结
本文探讨了日志系统的基本特性、实现日志系统的意义、方法和内部结构,并且给出了一种基于Java平台的日志系统的详细设计。同时也指出日志系统会向服务化、异步化的方向发展。作为一种方便的跟踪调试、数据恢复工具,应当提倡在适当的环境下对日志系统的使用。