log4cpp——使用01

log4cpp

参考

https://www.cnblogs.com/hrhguanli/p/3809023.html

源码使用最新的2.9.1版本,可能会和参考有出入

简单使用介绍

  1. 初始化一个appender。
  2. 设置layout,默认设置为BasicLayout
  3. 获取根Category &root
  4. 让appender对象附加到Category上
  5. [可选]可以通过additivity选择让appender独占,还是让Category有多个Appender。
  6. [可选]设置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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值