log4cpp源码下载点击此处下载
调用对应宏定义实现日志记录功能
流程
创建一个Appender,并指定其包含的Layout
从系统中得到Category的根,将Appender添加到该Category中;
设置Category的优先级
记录日志
关闭Category
使用原则
不要手动释放Category、Appender和Layout
同一个Appender不要加入多个Category,否则它会被释放多次从而导致程序崩溃
同一个Layout不要附着到多个Appender上,否则也会被释放多次导致程序崩溃
Layout
每个Appender都设置了一个Layout,用于定义这个Appender日志的格式
设置日志的格式
log4cpp中所有的Layout子类,一共有三个:BasicLayout、PatternLayout和SimpleLayout,其中SimpleLayout并不建议使用,而BasicLayout过于简单,因此如果程序员不自己扩展Layout的话,就只能 使用PatternLayout
SimpleLayout
BasicLayout
BasicLayout是log4cpp内置的一种格式
使用实例
使用setLayout()来设置BasicLayout,BasicLayout()是无参函数
输出
PatternLayout
可以自己设定格式
使用setConversionPattern函数来设置日志的输出格式
函数声明
void log4cpp::PatternLayout::setConversionPattern
(const std::string &conversionPattern) throw (ConfigureFailure) [virtual]
函数使用方法
类似C语言中的printf,使用格式化字符串来描述输出格式
-
格式化字符串具体含义
%c
category名
%d
日期
日期可以进一步的设置格式,用花括号包围
例如
%d{ &H: %M: %S, %l}
%d{%d %m %Y %H:%M:%S,%l}
若不设置具体日期格式,则默认格式为
“Wed Jan 02 02:03:55 1980”
%m //消息 %n //换行符 %p //优先级名 %r //从layout被创建后的毫秒数 %R //从1970年1月1日0时开始到目前为止的和秒数 %u //进程开始到目前为止的秒数 %x //NDC
可以将setConversionPattern的参数设置为"%d: %p %c %x: %m%n"
具体含义
“时间: 优先级 Category NDC: 消息 换行”
使用实例
log4cpp::PatternLayout* pLayout = new log4cpp::PatternLayout();
pLayout->serConversionPattern("%d: %p %c %s: %m%n");
osAppender->setLayout(pLayout);//一个appender对应一个Layout
日志对应内容
Appender
负责指向日志的目的地
-
按日志的目的地分类
log4cpp::OstreamAppender
构造函数原型
OstreamAppender(const string &name, ostream *stream)
参数列表
name
指定OstreamAppender的名称
stream
指定它关联的流的指针
添加Appender
使用实例
//首先创建一个Appender log4cpp::OstreamAppender& osAppender = new log4cpp::OstreamAppender("osAppender", &cout); log4cpp::PatternLayout* pLayout = new log4cpp::PatternLayout(); pLayout->setConversionPattern("%d: %p %c : %m%n"); osAppender->setLayout(pLayout); log4cpp::Category& root = log4cpp::Category::getRoot(); log4cpp::Category& infoCategory = root.getInstance("infoCategory"); //调用addAppender方法添加一个Appender infoCategory.addAppender(osAppender); infoCategory.setPriority(log4cpp::Priority::INFO); //只有优先级高于INFO的元素才能输出
先使用构造函数声明,再添加appender到category中
OstreamAppender用setLayout()函数设置输出格式
log4cpp::FileAppender
构造函数原型
//使用文件名写入 FileAppender(const std::string &name, const std::string &fileName, bool append=true, mode_t mode=00644); //使用文件描述符写入 FileAppender(const std::string &name, int fd);
一般仅使用前两个参数,即“名称”和“日志文件名”,第三个参数指示是否在日志文件后继续记入日志,还是清空原日志文件再记录,第四个参数说明文件的打开方式
log4cpp::RollingFileAppender
构造函数原型
RollingFileAppender (const std::string &name, const std::string &fileName, size_t maxFileSize=10 *1024 *1024, unsigned int maxBackupIndex=1, bool append=true, mode_t mode=00644);
它与FileAppender的创建函数很类似,但是多了两个参数:maxFileSize指出了回卷文件的最大值(单位为byte);maxBackupIndex指出了回卷文件所用的备份文件的最大个数
备份文件
用来保存回滚文件中因为空间不足未能记录的日志,备份文件的大小仅比回卷文件的最大值大1kb
只有一个备份文件装满了,才会创建一个新的备份文件
如果maxBackupIndex取值为3,则回卷文件会有三个备份文件,其名称分别是rollwxb.log.1,rollwxb.log.2和roolwxb.log.3,大小为101kb。
如果maxBackupIndex取值为0或者小于0,则回卷文件功能会失效,其表现如同FileAppender一样,不会有大小的限制,这也许是一个bug
FileAppender和RollingFileAppender之间的关系与区别
FileAppender和RollingFileAppender是log4cpp中最常用的两个 Appender,其功能是将日志写入文件中。它们之间唯一的区别就是前者会移至在文件中记录日志(直到操作系统承受不了的位置),而后者会在文件长度到达指定值时循环记录日志,文件长度不会超过指定值(默认的指定值是10M byte)
-
所有可以直接使用的Appender列表
log4cpp::IdsaAppender
发送到IDS
log4cpp::FileAppender
输出到文件
log4cpp::RollingFileAppender
输出到回卷文件,即当文件到达某个大小后回卷
回卷这个词来自磁带机年代,把”磁带绕回到磁带开始处“
即到达某个大小后回到文件头写入
log4cpp::OstreamAppender
输出到一个ostream类
log4cpp::RemoteSyslogAppender
输出到远程syslog服务器
log4cpp::StringQueueAppender
输出到内存队列
将日志记录到一个字符串队列中
_queue变量是StringQueueAppender类中用于具体存储日志的内存队列
StringQueueAppender的使用方法与OstreamAppender类似,其创建函数值接受一个参数”名称“,记录完成后需要程序员自己从队列中取出每条日志
使用实例
log4cpp::StringQueueAppender* strQAppender = new log4cpp::StringQueueAppender("strQAppender"); strQAppender->serLayout(new log4cpp::BasicLayout); log4cpp::Category& root = log4cpp::Category::getRoot(); root.addAppender(strQAppender); root.setPriority(log4cpp::Priority::DEBUG); root.error("Hello log4cpp in a Error Message!"); root.warn("Hello log4cpp in a Warning Message!"); cout << "Get message from Memory Queue!" << endl; cout << "-----------------------------------" << endl; queue<string>&myStrQ = strQAppender->getQueue(); while(!myStrQ.empty()) { cout << myStrQ.front(); myStrQ.pop(); } log4cpp::Category::shutdown(); return 0;
创建StringQueueAppender,然后添加如类中,最后在用StringQueueAppener的成员函数getQueue()返回数据成员容器queue
log4cpp::SyslogAppender
输出到本地syslog
Syslog是类Unix系统的一个核心服务,用来提供日志服务,在Windows系统中并没有直接提供支持,当然可以用相关工具提供Windows系统中的syslog服务
log4cpp::Win32DebugAppender
发送到缺省系统调试器
log4cpp::NTEventLogAppder
发送到win事件日志
Category
负责向日志中写入信息
负责记录日志,优先级
当日志的优先级大于等于Category时,该日志才会被记录,否则会被忽略掉
一个Category可以对应多个Appender
一个Appender对应一个Layout
回收空间
函数说明
该函数将会从Category上删除所有的Appender
函数原型
void log4cpp::Category::shutdown();
获取根日志
函数原型
Category & Category::getRoot();
获取子日志
函数原型
Catrgory & Category::getInstance(const std::string &name);
使用实例
#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";
Category::shutdown();
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
使用log4cpp::Category::getRoot()可以得到根Category。在大多数情况下,一个应用程序只需要一个日志种类(Category),但是有时也会用到多个Category,此时可以使用根Category的getInstance方法来得到子Category。不同的子Category用于不同的场合。
Priority
每个category都有一个优先级,该优先级可以有setPriority方法设置
设计日志优先级
数字越小,优先级越高
typedef enum {
EMERG = 0,
FATAL = 0,
ALERT = 100,
CRIT = 200,
ERROR = 300,
WARN = 400,
NOTICE = 500,
INFO = 600,
DEBUG = 700,
NOTSET = 800,
} PriorityLevel;
设置优先级
setPriority函数
setPriority()是category类的成员函数
函数说明
设置日志记录的级别,使得日志只记录优先级比所指定优先级级别更高的记录
函数声明
virtual void setPriority(Priority::Value priority)
throw (std::invalid_argument)
参数列表
priority
输入形式为:log4cpp::Priority::<优先级>
使用实例
log4cpp::OstreamAppender* osAppender1 =
new log4cpp::OstreamAppender("osAppender1", &cout);
osAppender1->setLayout(new log4cpp::BasicLayout());
log4cpp::OstreamAppender* osAppender2 =
new log4cpp::OstreamAppender("osAppender2", &cout);
osAppender2->setLayout(new log4cpp::BasicLayout());
log4cpp::Category& root = log4cpp::Category::getRoot();
//设置该Category只记录DEBUG级以上的消息
root.setPriority(log4cpp::Priority::DEBUG);
log4cpp::Category& sub1 = root.getInstance("sub1");
sub1.addAppender(osAppender1);
sub1.setPriority(log4cpp::Priority::DEBUG);
seb1.error("sub error");
Category、Appender和Layout三者的关系
系统中可以有多个Category,它们都是继承自同一个根,每个Category负责记录自己的日志;每个Category可以添加多个Appender,每个Appender指定了一个日志的目的地,例如文件、字符流或者Windows日志,当Category记录一条日志时,该日志被写入所有附加到此Category的Appender;每个Appender都包含一个Layout,该Layout定义了这个Appender上日志的格式
log4cpp的日志记录
函数原型
void debug(const std::string &message) throw();
void info(const std::string &message) throw();
void notice(const std::string &message) throw();
void warn(const std::string &message) throw();
void error(const std::string &message) throw();
void crit(const std::string &message) throw();
void alert(const std::string &message) throw();
void emerg(const std::string &message) throw();
void fatal(const std::string &message) throw();
一个封装好的c++日志类
头文件
mylogger.hpp
#include "log4cpp/Category.hh"
#include "log4cpp/Appender.hh"
#include "log4cpp/RollingFileAppender.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/PatternLayout.hh"
#include "log4cpp/Priority.hh"
#include <string>
#include <stdio.h>
#include <pthread.h>
#define LOG(level, msg) \
{ \
char _msg[1024]; \
sprintf(_msg, "%s [%s@%s,%d] ", \
msg, __func__, __FILE__, __LINE__ ); \
MyLogger::get_MyLogger()->level(_msg); \
}
using std::string;
using namespace log4cpp;
class MyLogger {
public:
static MyLogger * get_MyLogger();
void Logger_Init();
void AddAppender(const string & path);
void SetPriority(Priority::Value priority);
void warn(const char *msg);
void error(const char *msg);
void debug(const char *msg);
void info(const char *msg);
static void destroy();
private:
MyLogger();
~MyLogger()
{}
private:
static pthread_mutex_t * mutex;
static MyLogger * _pMylogger;
Category * root;
};
实现文件
//mylogger.cc
#include "mylogger.hpp"
MyLogger * MyLogger::_pMylogger = nullptr;
pthread_mutex_t * MyLogger::mutex = nullptr;
MyLogger * MyLogger::get_MyLogger() {
if(mutex == nullptr) {
mutex = new pthread_mutex_t;
pthread_mutex_init(mutex, NULL);
}
if(_pMylogger == nullptr) {
pthread_mutex_lock(mutex);
if(_pMylogger == nullptr) {
_pMylogger = new MyLogger();
}
pthread_mutex_unlock(mutex);
}
return _pMylogger;
}
MyLogger::MyLogger() {
Logger_Init();
SetPriority(Priority::DEBUG);
}
void MyLogger::Logger_Init() {
OstreamAppender * OsAppender = new OstreamAppender("console", &std::cout);
PatternLayout * pLayout = new PatternLayout();
pLayout->setConversionPattern("%d [%c] [%p] %m%n");
OsAppender->setLayout(pLayout);
root = &Category::getRoot();
root->setAppender(OsAppender);
}
void MyLogger::AddAppender(const string & path) {
RollingFileAppender * RAppender = new RollingFileAppender("Rollingfile", path);
PatternLayout * pLayout = new PatternLayout();
pLayout->setConversionPattern("%d [%c] [%p] %m%n");
RAppender->setLayout(pLayout);
root->addAppender(RAppender);
}
void MyLogger::SetPriority(Priority::Value priority) {
root->setPriority(priority);
}
void MyLogger::warn(const char * msg) {
root->warn(msg);
}
void MyLogger::error(const char * msg) {
root->error(msg);
}
void MyLogger::debug(const char * msg) {
root->debug(msg);
}
void MyLogger::info(const char * msg) {
root->info(msg);
}
void MyLogger::destroy() {
if(_pMylogger) {
Category::shutdown();
delete _pMylogger;
delete mutex;
}
}
测试用例
//main.cc
#include "mylogger.hpp"
int main() {
MyLogger * mylogger = MyLogger::get_MyLogger();
mylogger->AddAppender("main.log");
LOG(info, "root info");
LOG(error, "root error");
LOG(warn, "root warn");
LOG(debug, "root debug");
MyLogger::destroy();
return 0;
}