大家好,又见面了,我是你们的朋友全栈君。
1、log4cpp的下载安装与配置
官方网站http://log4cpp.sourceforge.net/有下载地址,安装过程配置选项及测试用例。Linux下将下载好的tar包解压到/usr/local/下运行./configure(如有需要添加相关配置选项),使用make编译,使用make check进行检测,使用make install安装,使用之前的相关命令安装好之后在/usr/local/include/和/usr/local/lib/文件夹下会有相关的文件。使用g++编译测试用例时需要链接相关的库文件,根据提示进行链接-llog4cpp-lpthread。根据测试用例的相关信息,查看安装是否成功。
2、由测试用例来看log4cpp的处理流程
2.1 几个重要的概念
Category(种类)负责向日志中写入信息
Appender(附加目的地)负责制定日志的目的地。例如FileAppender、RollingFileAppender 、OstreamAppender、StringQueueAppender、 Win32DebugAppender、 NTEventLogAppender
Layout(布局)负责设定日志的格式。例如BasicLayout 、SimpleLayout、 PatternLayout
Priority(优先级)
NDC(嵌套的诊断上下文)2.2 log4cpp记录日志的原理
每个Category都有一个优先级,该优先级可以由setPriority方法设置,或者从其父Category中继承而来。每条日志也有一个优先级。当 Category记录该条日志时,若日志的优先级高于Category的优先级时,该日志将被记录,否则被忽略
以下是在priority.hh中定义的相关优先级,数字越小优先级越高typedef enum {EMERG = 0, FATAL = 0, ALERT = 100, CRIT = 200, ERROR = 300, WARN = 400, NOTICE = 500, INFO = 600, DEBUG = 700, NOTSET = 800 } PriorityLevel;
2.3 测试用例
// main.cpp #include "log4cpp/Category.hh" #include "log4cpp/Appender.hh" #include "log4cpp/FileAppender.hh" #include "log4cpp/OstreamAppender.hh" #include "log4cpp/Layout.hh" #include "log4cpp/BasicLayout.hh" #include "log4cpp/Priority.hh" int main(int argc, char** argv) { log4cpp::Appender *appender1 = new log4cpp::OstreamAppender("console", &std::cout); appender1->setLayout(new log4cpp::BasicLayout()); log4cpp::Appender *appender2 = new log4cpp::FileAppender("default", "program.log"); appender2->setLayout(new log4cpp::BasicLayout()); log4cpp::Category& root = log4cpp::Category::getRoot(); root.setPriority(log4cpp::Priority::WARN); root.addAppender(appender1); log4cpp::Category& sub1 = log4cpp::Category::getInstance(std::string("sub1")); sub1.addAppender(appender2); // use of functions for logging messages root.error("root error"); root.info("root info"); sub1.error("sub1 error"); sub1.warn("sub1 warn"); // printf-style for logging variables root.warn("%d + %d == %s ?", 1, 1, "two"); // use of streams for logging messages root << log4cpp::Priority::ERROR << "Streamed root error"; root << log4cpp::Priority::INFO << "Streamed root info"; sub1 << log4cpp::Priority::ERROR << "Streamed sub1 error"; sub1 << log4cpp::Priority::WARN << "Streamed sub1 warn"; // or this way: root.errorStream() << "Another streamed error"; return 0; }
1352973121 ERROR : root error 1352973121 ERROR sub1 : sub1 error 1352973121 WARN sub1 : sub1 warn 1352973121 WARN : 1 + 1 == two ? 1352973121 ERROR : Streamed root error 1352973121 ERROR sub1 : Streamed sub1 error 1352973121 WARN sub1 : Streamed sub1 warn 1352973121 ERROR : Another streamed error
流程分析:
1、
创建Appender并指定其包含的Layout(本例中创建了OstreamAppender的appender1和FileAppender的appender2,它们指定的Layout都是BasicLayout)
2、从系统中得到Category的根,分别将Appender添加到相应的Category中(appender1添加到了root,appender2添加到了sub1)
3、设置Category的优先级(对root设置了优先级为WARN)
4、记录日志
5、内存释放(Log4cpp使用了一个内部类来管理这些对象。此类的名称是HierarchyMaintainer,它负责管理Category的继承关系,在程序结束时,HierarchyMaintainer会依次释放所有Category,而Category则会依次释放拥有的有效Appender,Appender则会释放所有附属的Layout。)
由结果可以看出由于root设置了优先级为WARN,日志消息ERROR和INFO中只有ERROR可以打印出,sub1没有设置优先级,ERROR和WARN都可以显示出。3、Layout
Layout规定了日志消息的显示格式,在源代码中有多个类来描述显示格式。它们之间的关系如下图:
基类Layout是一个抽象类,派生类BasicLayout和SimpleLayout以及PatternLayout都继承自它。在PatternLayout类中使用
setConversionPattern函数
来设置日志的输出格式。
函数原型为void setConversionPattern(string& conversionPattern) throw(ConfigureFailure);该函数的参数类型为string,目的是使用格式化的字符串来描述输出格式。具体含义如下:
%c category;(CategoryNameComponent)
%d 日期;(TimeStampComponent)日期可以进一步的设置格式,用花括号包围,例如%d{%H:%M:%S,%l} 或者 %d{%d %m %Y%H:%M:%S,%l}。如果不设置具体日期格式,则如下默认格式被使用“Wed Jan 02 02:03:55 1980”。日期的格式符号与ANSI C函数strftime中的一致。但增加了一个格式符号%l,表示毫秒,占三个十进制位。
%m 消息;(MessageComponent)
%n 换行符,会根据平台的不同而不同,但对于用户透明;
%p 优先级(PriorityComponent)
%r 自从layout被创建后的毫秒数(MillisSinceEpochComponent);
%R 从1970年1月1日0时开始到目前为止的秒数(SecondsSinceEpochComponent);
%u 进程开始到目前为止的时钟周期数(ProcessorTimeComponent);
%x NDC(NDCComponent)。
让我们看看源代码是如何使用这些格式化字符串的
while(...){ ... switch (ch) { case '%': literal += ch; break; case 'm': component = new MessageComponent(); break; case 'n': { std::ostringstream endline; endline << std::endl; literal += endline.str(); } break; case 'c': component = new CategoryNameComponent(specPostfix); break; case 'd': component = new TimeStampComponent(specPostfix); break; case 'p': component = new PriorityComponent(); break; case 'r': component = new MillisSinceEpochComponent(); break; case 'R': component = new SecondsSinceEpochComponent(); break; case 't': component = new ThreadNameComponent(); break; case 'u': component = new ProcessorTimeComponent(); break; case 'x': component = new NDCComponent(); break; ... }
在PatternLayout类中内嵌了PatternComponent类,MessageComponent、CategoryNameComponent...等这些类分别继承于PatternComponent类。PatternComponent中定义了append函数。
函数原型为virtual void append(ostringstream& out, const LoggingEvent& event) = 0;该函数的基本功能是将日志事件的相关信息以某种方式写入ostringstream流中。不同的Component继承了PatternComponent都会按照各自的方式来实现append函数
4、Appender
主要介绍以下Appender:
log4cpp::FileAppender
// 输出到文件 FileAppender(const string& name, const string& fileName, bool append = true, mode_t mode = 00644);
FileAppender(const string& name, int fd);
第一个构造函数中参数含义分别指的是appender的名字,日志文件的名字,在日志文件之后记录日志还是清空日志文件再记录,文件的打开方式。第二个构造函数参数的含义是appender的名字,日志文件的文件描述符
log4cpp::RollingFileAppender
// 输出到回卷文件,即当文件到达某个大小后回卷 RollingFileAppender(const string& name, const string& fileName,size_t maxFileSize = 10*1024*1024, unsigned int maxBackupIndex = 1, bool append = true, mode_t mode = 00644);
maxFileSize表示回滚文件的最大值,当文件大小超过该值10M时,回滚记录。maxBackupIndex指出了回滚文件所用的备份文件的最大个数。
log4cpp::OstreamAppender
// 输出到一个ostream类 OstreamAppender(const string& name, ostream* stream);第一个参数指定appender的名字,第二个参数指定关联的流指针
log4cpp::StringQueueAppender
// 内存队列 StringQueueAppender(const string& name);参数为appender指定名字,功能是将日志记录到一个字符串队列中,通过相关的方法对queue进行操作
log4cpp::Win32DebugAppender
// 发送到缺省系统调试器 Win32DebugAppender(const string& name);参数为appender指定名字
log4cpp::NTEventLogAppender
//发送到win事件日志 该Appender可以将日志发送到windows的日志,在运行程序后可以打开windows的计算机管理->系统工具->事件查看器->应用程序。
NTEventLogAppender(const std::string& name, const std::string& sourceName);第一个参数指出appender的名字,第二个参数指出日志文件的名字。
5、Category
该类中Category的构造函数被protected修饰,在类外无法构造对象,类中提供了两个static函数构造对象,分别是getRoot和getInstance。在大多数情况下,一个应用程序只需要一个日志种类(Category),但是有时也会用到多个Category,此时可以使用根Category的getInstance方法来得到子Category。不同的子Category用于不同的场合。6、NDC(nested DiagnosticContext)
嵌套的诊断上下文,A Nested Diagnostic Context, or NDC in short, is an instrument to distinguish interleaved(交错) log output from different sources. Log output is typically interleaved when a server handles multiple clients near-simulatanously.(几乎同时的)Interleaved log output can still be meaningful if each log entry from different contexts had a distinctive stamp(标记). This is where NDCs come into play.
NDC是一种用来区分不同源代码中交替出现的日志的手段。当一个服务端程序同时记录好几个并行客户时,输出的日志会混杂在一起难以区分。但如果不同上下文的日志入口拥有一个特定的标识,则可以解决这个问题。NDC就是在这种情况下发挥作用。注意NDC是以线程为基础的,每个线程拥有一个NDC,每个NDC的操作仅对执行该操作的线程有效。
Note that NDCs are managed on a per thread basis. NDC operations such as <code>push</code>, <code>pop</code>, <code>clear</code>, <code>getDepth</code> and <code> setMaxDepth</code> affect the NDC of the current thread only. NDCs of other threads remain unaffected.
下图指出了类中函数的调用关系:
常用的一些静态函数xxx()内部首先调用getNDC()函数得到一个NDC对象,之后继续调用对应的_xxx()虚函数。例如:
void NDC::clear() { getNDC()._clear(); }
几乎所有静态函数的都通过上面这个例子来实现,所有虚函数的操作对象都是_stack,它是一个用vector容器模拟的栈。
参考:
Log4cpp介绍及使用http://blog.csdn.net/kingskyleader/article/details/7320826
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/158398.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...