zl程序教程

您现在的位置是:首页 >  工具

当前栏目

log4cpp学习

学习 log4cpp
2023-06-13 09:12:30 时间

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

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
 
                                                        发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/158398.html原文链接:https://javaforall.cn
如果您是在找激活码,但输入激活码后激活失败,最新激活码地址:https://javaforall.cn/127239.html