log4cpp
文章目录
参考
https://www.cnblogs.com/hrhguanli/p/3809023.html
源码使用最新的2.9.1版本,可能会和参考有出入
简单使用介绍
- 初始化一个appender。
- 设置layout,默认设置为BasicLayout
- 获取根Category &root
- 让appender对象附加到Category上
- [可选]可以通过additivity选择让appender独占,还是让Category有多个Appender。
- [可选]设置Category的优先级
- 允许存在多个Category,但rootCategory只有一个,且已经被实例化。
- 每个Category可以有多个Appender
- 每个Appender只能有一个Layout
//simpleTest.cc
#include <iostream>
#include <log4cpp/OstreamAppender.hh>
#include <log4cpp/Category.hh>
using namespace std;
using namespace log4cpp;
int main()
{
OstreamAppender *os = new OstreamAppender("os", &cout);
os->setLayout(new BasicLayout);
Category &root = Category::getRoot();
root.setAdditivity(false); // 清空旧的addAppender
root.setPriority(Priority::DEBUG);
root.addAppender(os);
root.info(" info message !!!");
}
介绍
log4cpp的核心概念如下
Appender:用来指定日志输出的目的地
Layout:用来指定日志的输出格式
Priority:用来指定日志记录的级别,只有消息的级别大于日志的级别,才会被记录——请查看测试1:日志级别测试
Category:真正去干记录日志事情的东西
名词说明
优先级:指info,warn,fatal等日志类别
Category:指的是Category的name
Appender
Appender可以使用的有很多,但是目前以我的能力,我想关心的只有如下四个类
- FileAppender —— 发送到文件
- RollingFileAppender —— 发送到回卷文件,当文件到达某个大小后回卷
- OstreamAppender —— 发送到一个ostream类
- StringQueueAppender —— 发送到一个内存队列
每个Category可以add多个Appender。
每个Appender只能添加一个Layout,设置新的Layout会顶替之前的Layout。
OstreamAppender
主要源于C++的流操作符不是线程安全的。即
cout << “hello” << “world”;
这是俩次操作,而不是一次操作。因此输出的日志信息,在多线程下会存在错误。
继承层次
Appender->Appender->Skeleton->LayoutAppender->OstreamAppender
在OstreamAppender.hh中只能看到构造函数
- 构造函数@:
OstreamAppender(const string& name, ostream* stream);
我们去它的父类LayoutAppender看看
默认情况下,设置的Layout为BasicLayout
源码:
typedef BasicLayout DefaultLayoutType;
...
LayoutAppender::LayoutAppender(const std::string& name) :
AppenderSkeleton(name),
_layout(new DefaultLayoutType()){}
如果我们对一个Appender作俩次setLayout,会将之前的Layout删除。
源码:
void LayoutAppender::setLayout(Layout* layout){
if(layout != _layout){
Layout *oldLayout = _layout;
_layout = (layout == NULL) ? new DefaultLayoutType() : layout;
delete oldLayout;
}
}
- setLayout@:参数指定一个Layout指针即可
其它的还需要再上一个父类查看,但没有看到我关心的。请查看测试2:OstreamAppender测试
StringQueueAppender
这个Appender的用处是将所有的信息存放到内存中,然后在我们需要的时候,可以将其取出,然后输出。在多线程编程的情况下,如果我们的任务本来就比较耗时,如果还去调用printf,则会陷入IO,导致线程频繁切换。
参考说放在程序结束时输出,但对于服务器来说,程序首先是永远不可能结束的。因此可以设置成一个定时事件,比如3s一次,将内存中的日志信息输出。但关键信息还是可能会缺失。
- 构造函数@:
StringQueueAppender(const string& name);
- getQueue@:
queue<string>& getQueue()
请查看测试3:tringQAppender测试
FileAppender && RollingFileAppender
FileAppender(const std::string& name,
const std::string& fileName,
bool append = true, mode_t mode = 00644);
- name@:Appender的名字
- fileName@:文件名字
- append@:bool值,true代表append,false代表重新写,如果文件存在的话。
- mode@:文件打开权限
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);
- maxFileSize@:达到这个文件大小时开始滚动
- maxBackupIndex@:保持最多几个备份文件
这里谈一下滚动文件以及备份文件的含义。
所谓滚动,就是当文件达到预期值,就会产生一个filename.1的备份文件。再次达到预期值,原来的filename.1就会变成filename.2,filename变成filename.1。那么这些就称为备份文件。maxBackupIndex就代表保持多少个备份文件。filename是原文件,filename.1, .2是备份文件。
这里的原意就是文件大小达到maxSize,我就把它换个名字,正规做法是压缩一下。然后重新记录一个新的log。
Layout
- BasicLayout: 时间戳 优先级 日志信息(NDC标签)
- SimpleLayout: 优先级 日志信息,基本没人用
- PatternLayout: 常用
前面俩个的输出基本都不是给人看的日志信息,只适合在很小的工程中使用
PatternLayout
这里主要介绍PatternLayout
PatternLayout查看参考应该能写出来一个差不多样子的日志输出,但是这样太麻烦。这里我们利用一个单例模式和define来简化一下。
请查看测试4:PatternLayout测试
Category
- 总是存在一个实例化的Category,可以通过静态类方法getRoot()获得。
- 如果希望获得多个Category,通过使用getInstance()获得。Category是呈现一种树的关系,只能获得subCategory,需要先获得root,然后去调用root.getInstance。
- 相同名字的Category只会存在一个。
输出日志信息,请查看测试5:Category测试
各种测试
测试1:日志级别测试
OstreamAppender *osAppender = new OstreamAppender("osAppender", &cout);
Category &root = Category::getRoot();
root.setAppender(osAppender);
root.setPriority(Priority::CRIT);
root.debug("debug message you cannot see");
root.error("error message you cannot see");
root.fatal("fatal message");
root.alert("alert message");
测试2:OstreamAppender测试
OstreamAppender *osAppender = new OstreamAppender("ostream", &cout);
SimpleLayout *simple = new SimpleLayout();
osAppender->setLayout(simple);
Category &root = Category::getRoot();
root.addAppender(osAppender);
root.error("hello");
// test setLayOut
BasicLayout *basic = new BasicLayout();
osAppender->setLayout(basic);
root.addAppender(osAppender);
root.error("hello");
测试3:StringQAppender测试
StringQueueAppender * strQAppender = new StringQueueAppender("strQAppender");
Category &root = Category::getRoot();
root.addAppender(strQAppender);
root.fatal("this is fatal message");
root.debug("this is debug message");
root.info("this is info message");
auto retQueue = strQAppender->getQueue();
while(!retQueue.empty())
{
cout << retQueue.front();
retQueue.pop();
}
测试4:PatternLayout测试
namespace singsing{
class Logger : boost::noncopyable
{
class Init
{
public:
Init() : root_(log4cpp::Category::getRoot())
{
OstreamAppender *osAppender = new OstreamAppender("os", &cout);
PatternLayout *pLayout = new PatternLayout();
pLayout->setConversionPattern("%d %p %c %x: %m%n");
osAppender->setLayout(pLayout);
root_.addAppender(osAppender);
}
log4cpp::Category &getLogger()
{
return root_;
}
private:
log4cpp::Category &root_;
};
public:
static log4cpp::Category & getInstance()
{
static Init init;
return init.getLogger();
}
protected:
Logger() = default;
~Logger() = default;
private:
};
}// end singsing
#define LOG_INFO(msg) \
singsing::Logger::getInstance().info("file:%s func:%s, line: %d message: %s", __FILE__, __FUNCTION__, __LINE__, msg)
#define LOG_DEBUG(msg) \
singsing::Logger::getInstance().debug("file:%s func:%s, line: %d message: %s", __FILE__, __FUNCTION__, __LINE__, msg)
#define LOG_WARN(msg) \
singsing::Logger::getInstance().warn("file:%s func:%s, line: %d message: %s", __FILE__, __FUNCTION__, __LINE__, msg)
int main()
{
LOG_INFO("info");
LOG_WARN("warn");
LOG_DEBUG("debug");
}
实际上可能还要添加设置优先级级别等方法。这里仅仅时给出一个参考方向。
测试5:Category
本测试取自官方文档
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;
}