最近因为项目的原因,开始研究log4cplus这个日志库。主要是把新版中增加的异步模式log用起来。异步log目前很少有人用,网上说明的资料比较少。看了很多源码,做了很多测试,走了些许弯路。因此打算把一点心得记录下来,为后面要用此功能的人增加一点参考资料。借此机会,正式开启我的技术博客之旅:)。
log4cplus通常有三种使用形式:stdout(打印到屏幕),文件日志和网络日志。最常用的是文件日志。本文主要介绍本地文件日志的用法。
1. 一个简单的例子
首先来看一个简单的例子:
int main(int argc, char* argv[]) { log4cplus::initialize(); log4cplus::PropertyConfigurator::doConfigure(LOG4CPLUS_TEXT("log4cplus.properties")); log4cplus::Logger logger = log4cplus::Logger::getInstance("global"); LOG4CPLUS_DEBUG(logger, LOG4CPLUS_TEXT("DEBUG LOG TEST")); LOG4CPLUS_INFO(logger, LOG4CPLUS_TEXT("INFO LOG TEST")); LOG4CPLUS_WARN(logger, LOG4CPLUS_TEXT("WARN LOG TEST")); LOG4CPLUS_ERROR(logger, LOG4CPLUS_TEXT("ERROR LOG TEST")); log4cplus::Logger::shutdown(); return 0; }
这个程序启动以后,首先执行initialize函数,做一些初始化工作:初始化线程本地变量及其清理函数,注册各种类型的Appender(最终打日志的类)。然后通过 doConfigure 去读取该程序所在目录下的日志的配置文件log4cplus.properties。
getInstance函数会通过global这个名字,从log4cplus.properties读取起对应的log选项中,返回一个log4cplus::Logger实例。该实例底层是一个共享指针,指向一个真正的log实例。此时,所有的log相关的初始化工作完成了,可以开始打log了。
程序使用了log4cplus提供的宏来打log。打完log,需要调用shutdown函数关闭log实例,以确保log被flush到磁盘上。
2. log的不同方式及其特点
log4cplus是通过不同的Appender来决定怎样打log的:
ConsoleAppender:stdout日志
FileAppender:普通文件日志
RollingFileAppender:滚动的文件日志
DailyRollingFileAppender:按天滚动的文件日志
AsyncAppender:异步日志
Log4jUdpAppender:网络日志
除了ConsoleAppender、AsyncAppender和Log4jUdpAppender,剩下的都是文件日志。这些Appender打的日志都是同步的,即打log的语句会等到log打完(log被发送到文件系统的缓冲区)才返回。如果某个时刻,系统突然打了非常大量的log,以至于
超过了系统磁盘的最大读写速度,这个时刻以后的打log的线程都会被阻塞。这会极大地减缓系统响应速度。
为了缓解这样的问题,通常有三种做法:减少线上系统的log量(甚至线上系统关闭log),打网络日志,使用log4cplus提供的异步日志。但是,对与有些比较重要的需要对账的线上系统,通常需要打很详细的log。为了不让log影响系统的响应速度,通常会打
网络log。而网络log需要额外的服务器来接收log,同时会占用一定的带宽。因此如果log不是特别重要的系统,通常可以使用异步log来缓解这个问题。
log4cplus的AsyncAppender是log4cplus-1.2.0版本库开始支持的一个新功能。AsyncAppender可以让你的系统以异步的方式来打log,从而提高系统的响应速度。在系统启动后,所有的系统log首先会发送到AsyncAppender的一个队列中去,
AsyncAppender会在系统启动时专门开一个线程来写程序发过来的log。通过这种方式将log的生产和落地放到不同的线程来完成。
但是,AsyncAppender并不是万灵药。如果系统生产日志的速度超过了写日志线程的消费日志的速度,AsyncApender的队列会被填满。如果队列满了,生产日志的线程的打log时候会被阻塞住,直到队列有空间放新的日志。因此,要想万无一失,最后还是打
网络log。
2.1 log4cplus的同步日志的配置
## synchronous log properties. log4cplus.logger.global = INFO, SA log4cplus.appender.SA=log4cplus::DailyRollingFileAppender log4cplus.appender.SA.Schedule=HOURLY log4cplus.appender.SA.DatePattern=%Y-%m-%d:%H log4cplus.appender.SA.File=/home/work/log/imserv/imserv.log log4cplus.appender.SA.MaxBackupIndex=100 log4cplus.appender.SA.BufferSize=131072 log4cplus.appender.SA.Append=true log4cplus.appender.SA.layout=log4cplus::PatternLayout log4cplus.appender.SA.layout.ConversionPattern=%D{%Y-%m-%d %H:%M:%S,%Q} [%t] %-5p %m%n
2.2 log4cplus的异步log的配置
## asynchronous log properties. log4cplus.logger.global = INFO, AA log4cplus.appender.AA=log4cplus::AsyncAppender log4cplus.appender.AA.QueueLimit=10000 log4cplus.appender.AA.Appender=log4cplus::DailyRollingFileAppender log4cplus.appender.AA.Appender.Schedule=HOURLY log4cplus.appender.AA.Appender.Threshold = INFO log4cplus.appender.AA.Appender.DatePattern=%Y-%m-%d-%H log4cplus.appender.AA.Appender.File=./logger_test.log log4cplus.appender.AA.Appender.ImmediateFlush=false log4cplus.appender.AA.Appender.MaxFileSize=1000MB log4cplus.appender.AA.Appender.MaxBackupIndex=100 log4cplus.appender.AA.Appender.Append=true log4cplus.appender.AA.Appender.layout=log4cplus::PatternLayout log4cplus.appender.AA.Appender.layout.ConversionPattern=%D{%Y-%m-%d %H:%M:%S,%Q} [%t] %-5p %m%n
2.3 log4cplus异步日志使用过程中需要注意的地方
程序退出前记得调用shutdown函数将所有的log flush到磁盘上。
1.2.0版本的log4cplus异步日志有BUG,会导致丢日志,下一个版本会修复。解决办法见:https://sourceforge.net/p/log4cplus/bugs/333/