1.Log4cpp使⽤
Log4cpp中主要包含Category(种类),Appender(附加器),Layout(布局),Priorty(优先级),NDC(嵌套的诊断上下⽂)。
Category、Appender与Layout三者的关系如下图:
2.安装
官网地址
:https://sourceforge.net/projects/log4cpp/files/latest/downloa解压:
tar zxf log4cpp-1.1.3.tar.gz
编译:
cd log4cpp
./configure
make
make check
sudo make install
sudo ldconfig
默认安装路径:
头⽂件:
/usr/local/include/log4cpp库⽂件:/usr/local/lib/liblog4cpp
测试范例
使⽤log4cpp的基本步骤如下:
1.实例化一个layout对象
2.初始化一个appender对象
3.把layout对象附着在appender对象
4.调用
log4cpp::Category::getInstance(“name”)。实例化一个category对象;5.把appender对象附加到category上,根据additivity的值取代其它appender或附加在其它appender后。
6.设置category的优先级
简单来说,上面存在这样一种关系:
layout–>appender–>category
#include “log4cpp/Category.hh” #include “log4cpp/FileAppender.hh” #include “log4cpp/BasicLayout.h int main(int argc,char *argv[]) { // 1实例化⼀个layout 对象 log4cpp::Layout *layout = new log4cpp::BasicLayout(); //2. 初始化⼀个appender 对象 log4cpp::Appender *appender = new log4cpp::FileAppender(“FileAppender”, “./test_log4cpp1.log”); // 3. 把layout对象附着在appender对象上 appender->setLayout(layout); //4. 实例化⼀个category对象 log4cpp::Category &warn_log = log4cpp::Category::getInstance(“qaa”); //5. 设置additivity为false,替换已有的appender warn_log.setAdditivity(false); //6 把appender对象附到category上 warn_log.setAppender(appender); //设置category的优先级,低于此优先级的⽇志不被记录 warn_log.setPriority(log4cpp::Priority::WARN); //记录⼀些⽇志 warn_log.info(“Program info which cannot be wirten”); warn_log.debug(“This debug message will fail to write”); warn_log.alert(“Alert info”); //其他记录⽇志⽅式 warn_log.log(log4cpp::Priority::WARN, “This will be a logged war ning”); log4cpp::Priority::PriorityLevel priority; bool this_is_critical = true; if (this_is_critical) priority = log4cpp::Priority::CRIT; else priority = log4cpp::Priority::DEBUG; warn_log.log(priority, “Importance depends on context”); warn_log.critStream()<<“This will show up << as”<<1<<“critical message”; // clean up and flush all appenders log4cpp::Category::shutdown(); return 0; }编译:g++ -o test_log4cpp1 test_log4cpp1.cpp -llog4cpp -lpthread
配置⽂件使用步骤
基本步骤:
1.读取解析配置文件。
2.实例化category对象。
3.正常使用这些category对象进行日志处理。
# ⽂件名: test_log4cpp2.conf # a simple test config #定义了3个category sub1, sub2, sub1.sub2 log4j.rootCategory=DEBUG, rootAppender log4j.category.sub1=A1 log4j.category.sub2=INFO log4j.category.sub1.sub2=ERROR, A2 # 设置sub1.sub2 的additivity属性 log4j.additivity.sub1.sub2=false #定义rootAppender类型和layout属性 log4j.appender.rootAppender=org.apache.log4j.ConsoleAppender log4j.appender.rootAppender.layout=org.apache.log4j.BasicLayout #定义A1的属性 log4j.appender.A1=org.apache.log4j.FileAppender log4j.appender.A1.fileName=A1.log log4j.appender.A1.layout=org.apache.log4j.SimpleLayout #定义A2的属性 log4j.appender.A2=org.apache.log4j.ConsoleAppender log4j.appender.A2.layout=org.apache.log4j.PatternLayout #log4j.appender.A2.layout.ConversionPattern=The message %m at time%d%n log4j.appender.A2.layout.ConversionPattern=%d %m %n// FileName: test_log4cpp2.cpp // Test log4cpp by config file. #include “log4cpp/Category.hh” #include “log4cpp/PropertyConfigurator.hh” int main(int argc, char *argv[]) { //1 读取解析配置⽂件 // 读取出错, 完全可以忽略,可以定义⼀个缺省策略或者使⽤系统缺省策略 // BasicLayout输出所有优先级⽇志到ConsoleAppender try{ log4cpp::PropertyConfigurator::configure((“./test_log4cpp2.conf”); }catch(log4cpp::ConfigureFailure& f){ std::cout << “Configure Problem “ <<f.what()<<std::endl; return -1; } // 2 实例化category对象 // 这些对象即使配置⽂件没有定义也可以使⽤,不过其属性继承其⽗category // 通常使⽤引⽤可能不太⽅便,可以使⽤指针,以后做指针使⽤ // log4cpp::Category* root = &log4cpp::Category::getRoot(); log4cpp::Category& root = log4cpp::Category::getRoot() ; log4cpp::Category& sub1 = log4cpp::Category::getInstance(std::string(“sub1”)); log4cpp::Category& sub3 = log4cpp::Category::getInstance(std::string(“sub1.sub2”)); // 3 正常使⽤这些category对象进⾏⽇志处理。 // sub1 has appender A1 and rootappender. sub1.info(“This is some info”); sub1.alert(“A warning”); // sub3 only have A2 appender. sub3.debug(“This debug message will fail to write”); sub3.alert(“All hands abandon ship”); sub3.critStream() << “This will show up << as “ << 1 << ” critical message”; sub3 << log4cpp::Priority::ERROR << “And this will be an error”; sub3.log(log4cpp::Priority::WARN, “This will be a logged warning”); return 0; }编译:g++ -o test_log4cpp2 test_log4cpp2.cpp -llog4cpp -lpthread
Category类
这个是归类的意思,具有树型结构,将日志按”区域”划分。Log4cpp有且只⼀个根Category,可以有多个子Category组成树型结构。Category具有Priority、additivity属性与若干方法。下面列出所有的属性与常用方法。
属性:Name
Category的名称。不可重复。
相关⽅法
virtual const std::string& getName() const throw();
属性:Priority
⽇志的优先级
对于根Category,必须指定Priority。(priority < Priority::NOTSET)
对于⾮根Category,可以不指定Priority,此时优先级继承⾃⽗Category。
对于⾮根Category,也可以指定Priority,此时优先级覆盖⽗Category的优先级。
//设置当前Category的优先级 virtual void setPriority(Priority::Value priority); //获取当前对象的优先级 virtual Priority::Value getPriority() const throw(); // 设置root Category的优先级 static void setRootPriority(Priority::Value priority); // 获取root Category的优先级 static Priority::Value getRootPriority() throw(); //获取当前category继承表中的优先级,如果当前的优先值没有设置的话,则找他的父亲。 //如果父亲没有找到的话,他会继续向上找,因为root Category的优先值默认为Priority::INFO //所以肯定能找到 virtual Priority::Value getChainedPriority() const throw(); // 返回当前拥有priority优先级 virtual bool isPriorityEnabled(Priority::Value priority) const throw();属性:additivity
每个Category都有一个additivity属性,该属性默认值为true。
如果值为true,则该Category的Appender包含了父Category的Appender。
如果值为false,则该Category的Appender取代了父Category的Appender。
相关⽅法
virtual void setAdditivity(bool additivity);
virtual bool getAdditivity() const throw();
属性:parent
上级Category。根Category的parent为空。
相关⽅法
virtual Category* getParent() throw();
virtual const Category* getParent() const throw();
⽅法:getRoot
静态⽅法。取得根Category。
static Category& getRoot();
⽅法:getInstance
静态⽅法。取得指定名称(参数name)的Category,如果不存在,则⾃动创建⼀个以name命名,parent为rootCategory,Priority为INFO的Category(说⽩了就是⼀个⼯⼚⽅法,不要和单例模式混了)。
static Category& getInstance(const std::string& name);
⽅法:exists
静态⽅法。判断是否存在指定名称(参数name)的Category。如果存在,则返回相应的Category(指针),否则返回空指针。
static Category* exists(const std::string& name);
⽅法:shutdownForced
静态⽅法。从所有的Category中移除所有的Appender,并且删除所有的Appender。(shutdown⽅法只是不使用Appender,并没有彻底从内存中销毁Appender)。
static void shutdownForced();
⽅法:Appender相关
//添加⼀个Appender到Category中。 // 该⽅法将把Appender的所有权交给Category管理 virtual void addAppender(Appender* appender); // 添加⼀个Appender到Category中。 // 但是该⽅法并不把Appender的所有权交给Category管理 virtual void addAppender(Appender& appender); // 获取指定名字的Appender(指针)。如果不存在则返回空指针 virtual Appender* getAppender(const std::string& name) const; // 获取所有的Appender,以指针的⽅式存储在set中 virtual AppenderSet getAllAppenders() const; // 删除所有的Appender virtual void removeAllAppenders(); // 删除指定的Appender virtual void removeAppender(Appender* appender); // 判断指定Appender是否被当前Category拥有所有权。如果是的话,在删除该Appender时,将同时销毁它。 virtual bool ownsAppender(Appender* appender) const throw();使⽤virtual void addAppender(Appender* appender);⽅法时,Category会接管appender的所有权,并确保会在合适的时机销毁它,所以不要再在外⾯调⽤delete⽅法去销毁它。所以不要轻易删除。
如下面代码:
log4cpp::Appender*appender = new log4cpp::OstreamAppender(“default”, &std::cout); root.addAppender(appender); delete appender; // Error! Dont delete it⽅法:⽇志相关
Category的⽇志输出⽅式有两种,⼀种是简单的传递std::string类型的字符串,⼀种是采⽤类似c api中的printf,可以格式化⽣成字符串。
// 以指定的优先级⽣成⽇志 virtual void log(Priority::Value priority,const std::string& message) throw(); //以指定的优先级与格式化⽣成⽇志,类似c api中的printf⽅法 virtual void log(Priority::Value priority, const char* stringFormat,…) throw(); // 格式化⽣成⽇志的⼀种变体,直接传递va_list参数 virtual void logva(Priority::Value priority, const char* stringFormat,va_list va) throw(); //除了以上三种通⽤⽇志三种⽅法外,Log4cpp还指供了多种预定义Priority的⽅法,如: void debug(const char* stringFormat, …) throw(); void info(const char* stringFormat, …) throw(); …..例⼦:
log4cpp::Category& rootCategory = log4cpp::Category::getRoot();
rootCategory.error(“This is error message\n”);
rootCategory.log(log4cpp::Priority::DEBUG, “This is %d message\n”, 8);
⽅法: CategoryStream
每个CategoryStream对象封装⼀种优先级的⽇志输出,并提供了对“<<”符号的重载函数。CategoryStream对象内部借由std::ostringstream来实现流式格式化输出⽣成⽇志消息(std::string)。
// 获取(⽣成)指定优先级的CategoryStream virtual CategoryStream getStream(Priority::Value priority); // 对运算符“<<”的重载。也是获取(⽣成)指定优先级的CategoryStream virtual CategoryStream operator<<(Priority::Value priority); // 快捷⽅法。还有infoStram()等预定义优先级的快捷⽅法。 inline CategoryStream debugStream(); …..CategoryStream只是作为临时对象,总在对象销毁时才会正真将日志输出(flush)。可以强制调用CategoryStream的flush方法,这样会不利于阅读,日志代码应该跟实际业务无关,代码行尽可能少,随时可以屏蔽日志功能。
例⼦:
log4cpp::Category& root = log4cpp::Category::getRoot();
root << log4cpp::Priority::ERROR << “This is error message”;
root.debugStream() << “This is ” << 8 << ” message”;
Appender类
Appender负责把日志写入相应设备,如控制台,文件,调试器,windows日志,syslog等。
比如下面输出到文件系统中。
FileAppender
作⽤:输出到⽂件系统。
构造函数:
FileAppender(const std::string& name, const std::string& fileName,bool append = true, mode_t mode = 00644);
DailyRollingFileAppender
功能:
一种特例化的FileApppender,⽂件系统以天为单位进⾏管理,当⽣成⽇志时的⽇期发⽣变化时,将会⽣成新的⽇志⽂件。
构造函数:
DailyRollingFileAppender(const std::string& name,const std::string& fileName,unsigned int maxDaysToKeep = maxDaysToKeepDefault,bool append = true,mode_t mode = 00644);
OstreamAppender
作用:输出到指定流,需要指定std:ostream对象,可以是std::cout或std::cerr或其它派生自std::ostream的流对象。
构造函数:OstreamAppender(const std::string& name, std::ostream* stream);
例子:
log4cpp::OstreamAppender * osAppender = new log4cpp::OstreamAppender(“osAppender”,&std::cout);
RemoteSyslogAppender
作⽤:输出到远程syslog系统。
构造函数:
RemoteSyslogAppender(const std::string& name,
const std::string& syslogName,
const std::string& relayer,
int facility = LOG_USER,
int portNumber = 514);
相关参数:
SmptAppender
作⽤:通过smtp协议发送到指定邮箱。
构造函数:
SmptAppender(const std::string& name, const std::string& host, const std::string& from,const std::string& to, const std::string& subject);
相关参数:
StringQueueAppender
作⽤:输出到内存/字符串队列。
构造函数:StringQueueAppender(const std::string& name);
相关参数:
SyslogAppender
作⽤:输出到本地syslog系统。
相关参数:
SyslogAppender(const std::string& name, const std::string& syslogName,int facility = LOG_USER);
BufferingAppender
作⽤:输出到缓存队列
Win32DebugAppender
作⽤:输出到Windows缺省调试器。
IdsaAppender
作⽤:输出到Idsa服务。
NTEventLogAppender
输出到Windows⽇志系统
Layout类
Layout控制输出⽇志的显示样式。Log4cpp内置了4种Layout。
PassThroughLayout
没有布局的“布局”,你让它写什么它就写什么,它不会为你添加任何东⻄,连换⾏符都懒得为你加。
SimpleLayout
简单布局。只有简单优先级输出,相当于PatternLayout格式化为:“%p: %m%n”。
BasicLayout
基本布局。添加“时间”、“优先级”、“种类”、“NDC”。相当于PatternLayout格式化“%R %p %c %x: %m%n”
PatternLayout
格式化布局。它的使⽤⽅式类似C语⾔中的printf,使⽤格式化它符串来描述输出格式。⽬前⽀持的转义定义如下:
%% – 转义字符%
%c – Category
%d – ⽇期,如%d{%H:%M:%S,%l}。⽇期的格式符号与ANSI C函数strftime中的⼀致。但增加了⼀个格式符号%l,表示毫秒,占三个⼗进制位。
%m – 消息
%n – 换⾏符;会根据平台的不同⽽不同,但对⽤户透明。
%p – 优先级
%r – ⾃从layout被创建后的毫秒数
%R – 从1970年1⽉1⽇开始到⽬前为⽌的秒数
%u – 进程开始到⽬前为⽌的时钟周期数
%x – NDC
%t – 线程id
Priority优先级
⽇志的优先级。Log4cpp内置了10种优先级。
typedef enum { EMERG = 0, FATAL = 0, ALERT = 100, CRIT = 200, ERROR = 300, WARN = 400, NOTICE = 500, INFO = 600, DEBUG = 700, NOTSET = 800 } PriorityLevel;取值越⼩,优先级越⾼。如⼀个Category的优先级为INFO(600),则包括EMEGR、FATAL、ALERT、CRIT、ERROR、WARN、INFO等⼩于或等于600的⽇志都会被输出,⽽DEBUG等⼤于600的⽇志则不会被输出。
注意:优先级是⼀个整数,不⼀定要只取上⾯的预定义值,⽐如说101也是可以。对于根Category,优先级不能⼤于或等于NOTSET(800)。
NDC
NDC是以线程为基础的,每个线程拥有且只有⼀个NDC。NDC的⼏个常⽤⽅法是push、pop、get、clear。
Push:把⼀个字符串压⼊NDC栈。
Pop:从NDC栈中退出上⼀次压⼊的字符串。
Get:取得当前NDC栈中的字符串,每次压⼊的字符串⽤空格隔开。
Clear:清空NDC栈中的字符串。
NDC⽤法可以在每个函数⼊⼝处调⽤NDC::push(__FUNCTION__); 在函数出⼝处调⽤NDC::pop(); 在PatternLayout中指定%x参数。这样就可以在⽇志中清晰的知道函数的调⽤情况。
这篇文章就分析到这里,欢迎关注,转发,点赞,收藏。