首页 > 编程语言 >easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(四)日志信息保存

easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(四)日志信息保存

时间:2022-11-26 18:23:57浏览次数:74  
标签:std el CLOG writer 运算符 源码 base 日志

目录

在上一篇中我们分析完了 CLOG日志输出 的流程,在结尾的时候我们提出了一个问题:

    CLOG(INFO, "default") << "This is a CLOG!";

CLOG 宏的流式输出是如何实现的?今天我们就来解答这个问题。

writer 类的输出运算符

    前面我们经过分析知道 CLOG(INFO, "default") 经过展开后相当于:

    el::base::Writer(el::Level::Info, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default")

    而 el:: base::Writerconstruct 接口返回的是对象本身即 writer 实例,也就是说这里相当于调用的是 writer 类的输出运算符,接下来我们看看 writer 类的输出运算符都做了什么:

    template <typename T>
    inline Writer &operator<<(const T &log)
    {
    #if ELPP_LOGGING_ENABLED
        if (m_proceed)
        {
            m_messageBuilder << log;
        }
    #endif // ELPP_LOGGING_ENABLED
        return *this;
    }
    
    inline Writer &operator<<(std::ostream &(*log)(std::ostream &))
    {
    #if ELPP_LOGGING_ENABLED
        if (m_proceed)
        {
            m_messageBuilder << log;
        }
    #endif // ELPP_LOGGING_ENABLED
        return *this;
    }

writer 类的流操控符

    writer 类重载的输出运算符有两个,一个是模板,一个是参数为 std:: ostream& (*log)(std:: ostream&) 类型的函数指针。
    对于 C++流操控符 有所了解的应该知道,我们平时使用 std::cout << std::endl; 其中的 std::endl 其实就是流操控符,它的类型实际上是一个函数模板的实例,函数模板类型为:

    template <class _Elem, class _Traits>
    std::basic_ostream<_Elem, _Traits> &std::endl(std::basic_ostream<_Elem, _Traits> &_Ostr);

    而 cout 的类型为 std::ostream,所以上面的模板经过实例化为:

std::ostream& std::endl<char, char_traits<char>>(std::ostream& _Ostr);

    所以上面的第二个参数类型为 std::ostream& (*log)(std::ostream&) 的输出运算符相当于让 writer 拥有了使用流操控符的能力。也就是说我们可以这样使用 writer

    el::base::Writer(el::Level::Info, __FILE__, __LINE__, ELPP_FUNC, el::base::DispatchAction::NormalLog).construct(1, "default") << std::endl;

    当然我们也可以定制自己的流操控符,这已经相当于输入输出流方面的内容了,与日志库关系不大,限于篇幅这里就不多说了。

    再回到上面这两个 writer 的重载的输出运算符上面来,其实现是一样的:

    #if ELPP_LOGGING_ENABLED
    // 启用输出日志
    if (m_proceed)
    {
        // 需要处理这条日志
        m_messageBuilder << log;
    }
    #endif // ELPP_LOGGING_ENABLED
    return *this;

    这里我们看到 writer 类的输出运算符实际上是委托给 m_messageBuilder 的输出运算符来实现的。

el:: base:: MessageBuilder 类

    而 m_messageBuilder 的类型为 el:: base::MessageBuilder,前面我们简单的提过,el:: base::MessageBuilder 用于支持各种类型的日志输出。

    el:: base::MessageBuilder 重载了各种类型的输出运算符,同时也支持流操控符(如 std::endl)。如:内置类型STL 相关类型、QT 相关类型、boost 相关类型、WXWIDGETS 相关类型以及 其他类型 的支持。

    el:: base::MessageBuilder 所有上述输出运算符实际上做的事情内部都是委托给了 m_logger 成员对应的 stream 流。通过 m_logger 对应的 stream 流最终将日志信息暂时保存起来。
    el:: base::MessageBuilderm_logger 成员是前面 writer 实例调用 constrcut 接口初始化 m_messageBuilder 时( m_messageBuilder.initialize(m_logger); )作为参数传递进去的来,

    el:: base:: MessageBuilder的输出运算符,我们随便看个基本类型的实现就一清二楚了:

    #define ELPP_SIMPLE_LOG(LOG_TYPE)                    \
        MessageBuilder &operator<<(LOG_TYPE msg)         \
        {                                                \
            m_logger->stream() << msg;                   \
            if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) \
            {                                            \
                m_logger->stream() << " ";               \
            }                                            \
            return *this;                                \
        }
    
    ELPP_SIMPLE_LOG(char)

    上面的这个宏 ELPP_SIMPLE_LOG(char) 扩展开为:

    MessageBuilder &operator<<(char msg)
    {
        m_logger->stream() << msg;
        if (ELPP->hasFlag(LoggingFlag::AutoSpacing))
        {
            m_logger->stream() << " ";
        }
        return *this;
    }

    关于 MessageBuilder 的更多的内容后面会作更详细的分析。

    而 m_loggerstream 流其实就是 base:: type::stringstream_t 其实就是标准库的字符串流.
    base:: type:: stringstream_t 实际是个类型别名,定义如下:
    下面的宏我省略了多余的部分,只看关键的部分。

    #if defined(ELPP_UNICODE)
    typedef std::wstringstream stringstream_t;
    #else
    typedef std::stringstream stringstream_t;
    #endif // defined(ELPP_UNICODE)

上面 CLOG 宏的所有这些分析用一句话总结就是:

writer 对象重载的输出运算符可以将要输出的信息保存到当前写日志对应的日志记录器的字符串流对象中,而 writer 对象 离开作用域时调用的析构函数最终实现了将日志记录器的字符串流对象中保存的这些信息输出到文件或者终端或者其他输出目的地。

CLOG 宏接口调用流程图

image

CLOG 宏的日志信息保存部分到这里就介绍完了,下一篇我们分析一下前面 CLOG 宏的流程中使用到的其他相关类。

标签:std,el,CLOG,writer,运算符,源码,base,日志
From: https://www.cnblogs.com/DesignLife/p/16927938.html

相关文章

  • 在CentOS编译Git源码
    Git是一个免费的开源分布式版本控制系统,旨在处理从小到小到的所有内容具有速度和效率的超大型项目。Git易于学习,占用空间很小,性能快如闪电。它超越了Subversion,CVS,Per......
  • CentOS7源码安装Nginx1.22
    CentOS7源码安装Nginx1.22一、安装下载源码包:wgethttp://nginx.org/download/nginx-1.22.1.tar.gz安装依赖:yum-yinstallgccmakepcrepcre-developensslope......
  • C# 使用Serilog日志框架
    Serilog是一款配置方便,使用灵活的日志框架,使用方法如下:1、日志输出到控制台,需要使用Nuget安装Serilog和Serilog.Sinks.Console两个包//初始化日志的共享实......
  • 删除5天前旧日志 以防日志占用大量硬盘空间
    ///<summary>///删除5天前旧日志以防日志占用大量硬盘空间///</summary>publicstaticvoidDelOverdueFiles(intdays=5){......
  • AFL源码分析(一)
    AFL源码分析(一)文章首发于:ChaMd5公众号​​https://mp.weixin.qq.com/s/E-D_M25xv5gIpRa6k8xOvw​​a.alf-gcc.c1.find_as这个函数的功能是获取使用的汇编器。首先获取环境......
  • 深入剖析Java Stream底层源码
    1Stream的使用在深入学习Stream的本质之前,我们需要先熟悉Stream的使用规则。对Stream有了整体的认识之后,才能更好的理解它的本质。1.1使用步骤使用Stream只需要遵循3......
  • SpringBoot中的日志框架(未完成)
    日志是什么?日志是一个记录事件的组件,无论是记录运行情况还是追踪线上问题,都需要分析日志。为什么需要日志框架?场景演变:1、最初时利用System.out.println("msg")来输出......
  • easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(三)日志输出
    目录Writer析构函数Writer::processDispatch接口Writer::triggerDispatch接口base::LogDispatcher::dispatch接口DefaultLogDispatchCallback::handle接口DefaultLogD......
  • JSP课设:学生选课系统(附源码+调试)
    JSP学生选课管理系统学生选课管理系统功能概述(1)登录模块分为两种角色:学生角色、教师角色(2)教师模块:选课管理功能为对课程信息(课程编号、名称、学分)进行添加、修改、删除操......
  • WordPress 主题教程 #5c:日志元数据
    日志元数据是从零开始创建WordPress主题系列教程的五篇的第三部分,今天我们将开始讲解日志的元数据(Postmetadata):日期(date),分类(categories),作者(author),评论数(numberofcomment......