log4cxx
1.简介
(1)Apache log4cxx当前是由Apache软件基金会进行维护。它是java中著名开源项目Apache log4j在c++中对应的日志框架。它是借助于APR(Apache Portable Runtime)的可在多平台进行移植的开源项目。
经验表明,日志是开发流程中一个重要的模块,有很多优点。可以提供程序运行的精确信息。一旦我们将日志写入代码中,日志的产生就不用我们人工干预了。
此外,我们可以将日志信息记录在稳定的媒介中供我们之后去分析。除了这些用途之外,一个有效丰富的日志模块还可以看作是一个审计工具。
当然日志也有它的一些缺点。它会降低程序运行速度。如果日志冗余,会一直刷屏。为了减轻这些顾虑,log4cxx就被立志于设计成一个可靠、快速和可扩展的模块。
(2)log4cxx包括三个组件,loggers,appenders和layouts。loggers定义一个logger实体,appenders设置日志输出到哪里,layouts设置日志以什么格式进行输出。
(3)具体参考log4cxx官方网站:http://logging.apache.org/log4cxx/
2.windows下编译
由于log4cxx运行在APR下,所以需要apr和apr-util开源库,可以在apache官网上下载。编译过程中遇到了一些问题,可以参考网上的一些博客。
例如:http://blog.sina.com.cn/s/blog_a459dcf501013tbn.html
3.log4cxx的性能
(1)log4cxx对系统性能的影响程度主要体现在以下几方面:
a、日志输出的目的地,输出到控制台的速度比输出到文件系统的速度要慢。
b、日志输出格式不一样对性能也会有影响,如简单输出布局(SimpleLayout)比格式化输出布局(PatternLayout)输出速度要快。可以根据需要尽量采用简单输出布局格式输出日志信息。
c、日志级别越低输出的日志内容就越多,对系统系能影响很大。
d、日志输出方式的不同,对系统系能也是有一定影响的,采用异步输出方式比同步输出方式性能要高。
e、每次接收到日志输出事件就打印一条日志内容比当日志内容达到一定大小时打印系能要低。
(2)不同日志输出方式的性能分析
日志线程发现需要记日志时独占缓存(与此同时各线程等待,此时各线程是被阻塞住的),从缓存中取出日志信息,获得输出流进行输出,将缓存解锁(各线程收到提醒,可以接着写日志了)。
众所周知,磁盘IO操作、网络IO操作、数据库操作等都是非常耗时的,日志输出的主要性能瓶颈也就是在写文件、写网络、写数据库的时候。日志是肯定要记的,而要采用异步方式记,也就只有将这些耗时操作从主线程当中分离出去才真正的实现性能提升,也只有在线程间同步开销小于耗时操作时使用异步方式才真正有效 !
现在我们接着分别来看看这几种日志输出方式:
a、将日志记录到本地文件:log4cxx本身有一个buffer处理入库,采用异步方式并不一定能提高性能(主要是如何配置好缓存大小);而线程间的同步开销则是非常大的!因此在使用本地文件记录日志时不建议使用异步方式。
b、将日志记录到JMS:JMS本身是支持异步消息的,如果不考虑JMS消息创建的开销,也不建议使用异步方式。(java中的log4j)
c、将日子记录到SOCKET:将日志通过Socket发送,纯网络IO操作不需要反馈,因此也不会耗时
d、将日志记录到数据库:众所周知数据库是几种方式中最耗时的,网络、磁盘、数据库事务,都使数据库操作异常的耗时,在这里采用异步方式入库倒是一个不错的选择。
e、将日志记录到SMTP:同数据库
(3)日志输出到文件的性能分析
log4cxx.logger.monitorLogger=INFO,monitorAppender
log4cxx.additivity.monitorLogger=false
log4cxx.appender.monitorAppender=org.apache.log4cxx.DailyRollingFileAppender
log4cxx.appender.monitorAppender.File=mtlogs/mt_log.txt
log4cxx.appender.monitorAppender.layout=org.apache.log4cxx.PatternLayout
log4cxx.appender.monitorAppender.layout.ConversionPattern=%m%n
log4cxx.appender.monitorAppender.DatePattern='.'yyyy-MM-dd-HH
log4cxx.appender.monitorAppender.BufferedIO=true
上面的日志输出配置,在当日志打印很频繁的时候,由于写文件时读写磁盘的速度小于日志打印速度,这样下来就会影响软件性能(在windows平台下会导致内存增长,猜测可能会将日志先缓存起来,再进行写文件操作)。
如果添加如下配置:
log4cxx.appender.monitorAppender.BufferedIO=true
log4cxx.appender.monitorAppender.BufferSize=8192
这个选项用于告诉log4cxx输出日志的时候采用缓冲的方式,而不是即时flush方式(log4cxx输出缓冲日志是以8K为单位的,因为磁盘的一个block为8K,这样可以减少碎片,也就是说假设你设置缓存为18K,log4cxx在16K(8K*2)的时候就会输出),而不是18K)。实现了缓存模式,即合并日志输出的模式,它先把所有的日志都放到一个buffer数组里,如果buffer满了,就把buffer里内容全部写入磁盘,这样可以大大减少调用磁盘的次数。那么问题来了:既然开启缓存模式能提高系统,那么log4cxx为什么没有默认开启缓存模式呢?个人猜测是因为缓存模式需要缓存填充满了才会写入磁盘,但是假如系统突然崩溃了,缓存中残留的数据没有写入磁盘,从而导致日志丢失。而系统崩溃时的日志往往是排查和定位问题的关键,所以大部分情况下日志的完整性更为重要。但是享受缓慢模式的高性能与日志的完整性真的是鱼与熊掌不可兼得吗?是不是可以通过捕获异常,在异常中将缓存日志写入文件中?
(4)日志输出采用异步方式性能分析
异步输出日志工作原理
AsyncAppender采用的是生产者消费者的模型进行异步地将Logging Event送到对应的Appender中。
a、 生产者:外部应用了log4cxx的系统的实时线程,实时将Logging Event传送进AsyncAppender里
b、 中转:Buffer和DiscardSummary
c、 消费者:Dispatcher线程和appenders
工作原理:
1) Logging Event进入AsyncAppender,AsyncAppender会调用append方法,在append方法中会去把logging Event填入Buffer中,当消费能力不如生产能力时,AsyncAppender会把超出Buffer容量的Logging Event放到DiscardSummary中,作为消费速度一旦跟不上生成速度,中转buffer的溢出处理的一种方案。
2)AsyncAppender有个线程类Dispatcher,它是一个简单的线程类,实现了Runnable接口。它是AsyncAppender的后台线程。
Dispatcher所要做的工作是:
1)锁定Buffer,让其他要对Buffer进行操作的线程阻塞。
2)看Buffer的容量是否满了,如果满了就将Buffer中的Logging Event全部取出,并清空Buffer和DiscardSummary;如果没满则等待Buffer填满Logging Event,然后notify Disaptcher线程。
3) 将取出的所有Logging Event交给对应appender进行后面的日志信息推送。
以上是AsyncAppender类的两个关键点:append方法和Dispatcher类,通过这两个关键点实现了异步推送日志信息的功能,这样如果大量的Logging Event进入AsyncAppender,就可以游刃有余地处理这些日志信息了。