https://www.cnblogs.com/hrhguanli/p/3809023.html
源码使用最新的2.9.1版本,可能会和参考有出入
//simpleTest.cc
#include
#include
#include
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可以使用的有很多,但是目前以我的能力,我想关心的只有如下四个类
每个Category可以add多个Appender。
每个Appender只能添加一个Layout,设置新的Layout会顶替之前的Layout。
主要源于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;
}
}
其它的还需要再上一个父类查看,但没有看到我关心的。请查看测试2:OstreamAppender测试
这个Appender的用处是将所有的信息存放到内存中,然后在我们需要的时候,可以将其取出,然后输出。在多线程编程的情况下,如果我们的任务本来就比较耗时,如果还去调用printf,则会陷入IO,导致线程频繁切换。
参考说放在程序结束时输出,但对于服务器来说,程序首先是永远不可能结束的。因此可以设置成一个定时事件,比如3s一次,将内存中的日志信息输出。但关键信息还是可能会缺失。
StringQueueAppender(const string& name);
queue& getQueue()
请查看测试3:tringQAppender测试
FileAppender(const std::string& name,
const std::string& fileName,
bool append = true, mode_t mode = 00644);
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);
这里谈一下滚动文件以及备份文件的含义。
所谓滚动,就是当文件达到预期值,就会产生一个filename.1的备份文件。再次达到预期值,原来的filename.1就会变成filename.2,filename变成filename.1。那么这些就称为备份文件。maxBackupIndex就代表保持多少个备份文件。filename是原文件,filename.1, .2是备份文件。
这里的原意就是文件大小达到maxSize,我就把它换个名字,正规做法是压缩一下。然后重新记录一个新的log。
前面俩个的输出基本都不是给人看的日志信息,只适合在很小的工程中使用
这里主要介绍PatternLayout
PatternLayout查看参考应该能写出来一个差不多样子的日志输出,但是这样太麻烦。这里我们利用一个单例模式和define来简化一下。
请查看测试4:PatternLayout测试
输出日志信息,请查看测试5:Category测试
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");
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");
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();
}
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");
}
实际上可能还要添加设置优先级级别等方法。这里仅仅时给出一个参考方向。
本测试取自官方文档
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;
}