log4cpp学习

log4cpp学习1、linux下log4cpp的下载安装配置http://log4cpp.sourceforge.net/官方网站有下载地址,安装过程配置选项及测试用例。将下载好的tar包解压到/usr/local/下运行./configure(如有需要添加相关配置选项),使用make编译,使用makecheck进行检测,使用makeinstall安装,使用之前的相关命令安装好之后在/usr

大家好,又见面了,我是你们的朋友全栈君。

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规定了日志消息的显示格式,在源代码中有多个类来描述显示格式。它们之间的关系如下图:

log4cpp学习

基类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.

下图指出了类中函数的调用关系:

log4cpp学习

常用的一些静态函数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账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号