首页 > 编程语言 >easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(五)其他相关类

easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(五)其他相关类

时间:2022-11-26 21:56:08浏览次数:72  
标签:std CLOG LOG ITERATOR ++ 源码 template 日志 ELPP

目录

在上一篇中我们分析了 CLOG日志信息保存 的流程,今天我们看看前面 CLOG 宏所使用到的一些相关类。

el::base::Writer类

    另一个 construct 接口

    //声明
    Writer &construct(Logger *logger, bool needLock = true);
    // 定义
    Writer &Writer::construct(Logger *logger, bool needLock)
    {
        m_logger = logger;
        // initializeLogger前面已经介绍过了,这里就不多说了
        initializeLogger(logger->id(), false, needLock);
        // 初始化m_messageBuilder
        m_messageBuilder.initialize(m_logger);
        return *this;
    }

    这个接口可以使用 Logger 作为参数来初始化 Writer 对象,而不必使用 Logger ID 作为参数在 RegisteredLoggers 当中查找,提高效率。

    这个接口主要用于使用 Logger 的类 printf 接口记录日志的场景中,后面介绍到这一块时,会详细分析。

el::base::NullWriter类

    这个类重载的两个输出运算符就直接返回了对象本身,什么也没做,也就相当于不输出日志了。

    class NullWriter : base::NoCopy
    {
    public:
        NullWriter(void) {}
    
        // Null manipulator
        inline NullWriter &operator<<(std::ostream &(*)(std::ostream &))
        {
            return *this;
        }
    
        template <typename T>
        inline NullWriter &operator<<(const T &)
        {
            return *this;
        }
    
        inline operator bool()
        {
            return true;
        }
    };

el::LogMessage类

    主要用于作为其他类的成员或者函数参数,日志相关信息的 wrapper 类,都是一些成员函数的获取接口,没什么可说的。

    class LogMessage
    {
    public:
        LogMessage(Level level, const std::string &file, base::type::LineNumber line, const std::string &func,
                   base::type::VerboseLevel verboseLevel, Logger *logger) : m_level(level), m_file(file), m_line(line), m_func(func),
                                                                            m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str())
        {
        }
        inline Level level(void) const
        {
            return m_level;
        }
        inline const std::string &file(void) const
        {
            return m_file;
        }
        inline base::type::LineNumber line(void) const
        {
            return m_line;
        }
        inline const std::string &func(void) const
        {
            return m_func;
        }
        inline base::type::VerboseLevel verboseLevel(void) const
        {
            return m_verboseLevel;
        }
        inline Logger *logger(void) const
        {
            return m_logger;
        }
        inline const base::type::string_t &message(void) const
        {
            return m_message;
        }
    
    private:
        Level m_level;                           // 日志级别
        std::string m_file;                      // 日志所在的源文件
        base::type::LineNumber m_line;           // 日志所在的行号
        std::string m_func;                      // 日志所在的函数
        base::type::VerboseLevel m_verboseLevel; // 日志对应的详细级别
        Logger *m_logger;                        // 日志对应的日志记录器
        base::type::string_t m_message;          // 日志的内容部分(消息格式指示符当中的%msg代表的内容)
    };

el::Logger类

    el:: Logger 被 el:: base:: writer 类使用,用于读取配置,不直接写日志
    在 easylogging++的总体设计 中介绍过 el:: Logger 的一些成员变量,这里分析其部分接口。

isValidId接口

    static const char *kValidLoggerIdSymbols =
        "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._";
    
    // 检验 logger id 是否有效
    bool Logger::isValidId(const std::string &id)
    {
        for (std::string::const_iterator it = id.begin(); it != id.end(); ++it)
        {
            if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it))
            {
                return false;
            }
        }
        return true;
    }

    base:: utils::Str 是 easylogging++内部封装的字符操作的工具类,日志记录器的 id 必须是 kValidLoggerIdSymbols 当中的字符

flush接口

    1、 void Logger:: flush(void);

    // 刷新日志记录器的所有日志级别的日志文件
    void Logger::flush(void)
    {
        ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels");
        base::threading::ScopedLock scopedLock(lock());
        base::type::EnumType lIndex = LevelHelper::kMinValid;
        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool
                                  {
                                      flush(LevelHelper::castFromInt(lIndex), nullptr);
                                      return false; // 返回false是为了让每个日志级别都能够flush
                                  });
    }

    上面 LevelHelper:: forEachLevel 的实现如下:

    // 从指定级别开始遍历,对于每个级别执行一些操作(fn)
    void LevelHelper::forEachLevel(base::type::EnumType *startIndex, const std::function<bool(void)> &fn)
    {
        base::type::EnumType lIndexMax = LevelHelper::kMaxValid;
        do
        {
            if (fn())
            {
                break;
            }
            *startIndex = static_cast<base::type::EnumType>(*startIndex << 1);
        } while (*startIndex <= lIndexMax);
    }

    //flush中调用LevelHelper::forEachLevel传递的fn为匿名lamba表达式:
        [&](void)
            ->bool
    {
        // 刷新日志记录器的指定日志级别的日志文件
        flush(LevelHelper::castFromInt(lIndex), nullptr);
        return false;
    }

    2、 void flush(Level level, base:: type:: fstream_t *fs);

    // 刷新日志记录器的指定日志级别的日志文件
    void Logger::flush(Level level, base::type::fstream_t *fs)
    {
        if (fs == nullptr && m_typedConfigurations->toFile(level))
        {
            // 获取日志级别对应的文件流
            fs = m_typedConfigurations->fileStream(level);
        }
        if (fs != nullptr)
        {
            // 文件存在则刷新文件
            fs->flush();
            // 重置日志级别对应的未刷新次数
            std::unordered_map<Level, unsigned int>::iterator iter = m_unflushedCount.find(level);
            if (iter != m_unflushedCount.end())
            {
                iter->second = 0;
            }
            // 检查日志级别对应的日志文件是否要进行日志回旋
            Helpers::validateFileRolling(this, level);
        }
    }

initUnflushedCount接口

    initUnflushedCount 主要用于初始化日志记录器的所有日志级别对应的日志文件的未刷新次数

    void Logger::initUnflushedCount(void)
    {
        m_unflushedCount.clear();
        base::type::EnumType lIndex = LevelHelper::kMinValid;
        LevelHelper::forEachLevel(&lIndex, [&](void) -> bool
                                  {
          m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0));
          return false; });
    }

    LevelHelper:: forEachLevel 的作用上面已经介绍过了,这里就不多说了。

el::base::MessageBuilder类

    用于支持各种类型的日志输出

成员变量

     Logger* m_logger; //日志记录器    
     const base::type::char_t* m_containerLogSeparator; //输出容器时,各个元素之间的分隔符。

成员函数

    主要是重载了各种类型的输出运算符,同时支持流操控符(如 std:: endl)

支持流操控符

    inline MessageBuilder &operator<<(std::ostream &(*OStreamMani)(std::ostream &))
    {
        m_logger->stream() << OStreamMani;
        return *this;
    }

支持内置类型

    // char
    ELPP_SIMPLE_LOG(char)
    // bool
    ELPP_SIMPLE_LOG(bool)
    // signed short
    ELPP_SIMPLE_LOG(signed short)
    // unsigned short
    ELPP_SIMPLE_LOG(unsigned short)
    // signed int
    ELPP_SIMPLE_LOG(signed int)
    // unsigned int
    ELPP_SIMPLE_LOG(unsigned int)
    // signed long
    ELPP_SIMPLE_LOG(signed long)
    // unsigned long
    ELPP_SIMPLE_LOG(unsigned long)
    // float
    ELPP_SIMPLE_LOG(float)
    // double
    ELPP_SIMPLE_LOG(double)
    // char*
    ELPP_SIMPLE_LOG(char *)
    // const char*
    ELPP_SIMPLE_LOG(const char *)
    // const void*
    ELPP_SIMPLE_LOG(const void *)
    // long double
    ELPP_SIMPLE_LOG(long double)

    ELPP_SIMPLE_LOG 宏,前面日志信息保存流程中已经介绍过了。

支持std::string 类型

    inline MessageBuilder &operator<<(const std::string &msg)
    {
        return operator<<(msg.c_str());
    }

支持std::wstring 类型

    inline MessageBuilder &operator<<(const std::wstring &msg)
    {
        return operator<<(msg.c_str());
    }

支持wchar_t*类型

    MessageBuilder &MessageBuilder::operator<<(const wchar_t *msg)
    {
        if (msg == nullptr)
        {
            m_logger->stream() << base::consts::kNullPointer;
            return *this;
        }
    #if defined(ELPP_UNICODE)
        m_logger->stream() << msg;
    #else
        // 非unicode时,将wchar*转为char*
        char *buff_ = base::utils::Str::wcharPtrToCharPtr(msg);
        m_logger->stream() << buff_;
        free(buff_);
    #endif
        if (ELPP->hasFlag(LoggingFlag::AutoSpacing))
        {
            m_logger->stream() << " ";
        }
        return *this;
    }

支持容器相关类型

    // 支持一个模板参数的容器
    #define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp)                                               \
        template <typename T>                                                                       \
        inline MessageBuilder &operator<<(const temp<T> &template_inst)                             \
        {                                                                                           \
            return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
        }
    
    // 支持2个模板参数的容器
    #define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp)                                               \
        template <typename T1, typename T2>                                                         \
        inline MessageBuilder &operator<<(const temp<T1, T2> &template_inst)                        \
        {                                                                                           \
            return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
        }
    
    // 支持3个模板参数的容器
    #define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp)                                             \
        template <typename T1, typename T2, typename T3>                                            \
        inline MessageBuilder &operator<<(const temp<T1, T2, T3> &template_inst)                    \
        {                                                                                           \
            return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
        }
    
    // 支持4个模板参数的容器
    #define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp)                                              \
        template <typename T1, typename T2, typename T3, typename T4>                               \
        inline MessageBuilder &operator<<(const temp<T1, T2, T3, T4> &template_inst)                \
        {                                                                                           \
            return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
        }
    
    // 支持5个模板参数的容器
    #define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp)                                              \
        template <typename T1, typename T2, typename T3, typename T4, typename T5>                  \
        inline MessageBuilder &operator<<(const temp<T1, T2, T3, T4, T5> &template_inst)            \
        {                                                                                           \
            return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \
        }

    上面这几个宏内部都是委托给 writeIterator 这个成员函数模板来实现的:

    template <class Iterator>
    MessageBuilder &writeIterator(Iterator begin_, Iterator end_, std::size_t size_)
    {
        m_logger->stream() << ELPP_LITERAL("[");
        for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_)
        {
            operator<<(*begin_);
            m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeparator : ELPP_LITERAL(""));
        }
        if (begin_ != end_)
        {
            m_logger->stream() << ELPP_LITERAL("...");
        }
        m_logger->stream() << ELPP_LITERAL("]");
        if (ELPP->hasFlag(LoggingFlag::AutoSpacing))
        {
            m_logger->stream() << " ";
        }
        return *this;
    }
    

    实现也不复杂,就是遍历容器对应的迭代器,依次保存在 m_logger 对应的 stream(字符串流对象)中。

    相关容器的宏使用:

    //STL容器
    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector)
    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list)
    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque)
    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set)
    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset)
    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map)
    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap)
    ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map)
    ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap)
    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set)
    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset)>>>     
    //boost容器
    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector)
    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector)
    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list)
    ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque)
    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map)
    ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map)
    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set)
    ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set)>>>     
    //QT容器
    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList)
    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector)
    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue)
    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet)
    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList)
    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack)>>>     
    //WXWIDGETS容器
    ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector)

    可以对照相关文档看这些容器为啥分别用的这些宏,主要是不同容器实际的模板参数个数的区别。
    对于其他的一些类型,el::base::MessageBuilder 提供了相应类型的输出运算符的重载,因为实现和上面类似,这里只是列出了声明:

    // std::pair
    template <class First, class Second>
    MessageBuilder &operator<<(const std::pair<First, Second> &pair_);
    // std::bitset
    template <std::size_t Size>
    MessageBuilder &operator<<(const std::bitset<Size> &bitset_);
    // std::array
    template <class T, std::size_t Size>
    inline MessageBuilder &operator<<(const std::array<T, Size> &array);
    // QString
    inline MessageBuilder &operator<<(const QString &msg);
    // QByteArray
    inline MessageBuilder &operator<<(const QByteArray &msg);
    // QStringRef
    inline MessageBuilder &operator<<(const QStringRef &msg);
    // qint64
    inline MessageBuilder &operator<<(qint64 msg);
    // quint64
    inline MessageBuilder &operator<<(quint64 msg);
    // QChar
    inline MessageBuilder &operator<<(QChar msg);
    // QLatin1String
    inline MessageBuilder &operator<<(const QLatin1String &msg);
    // QPair<First, Second>
    template <typename First, typename Second>
    MessageBuilder &operator<<(const QPair<First, Second> &pair_);
    // QMap<K, V>
    template <typename K, typename V>
    MessageBuilder &operator<<(const QMap<K, V> &map_);
    // QMultiMap<K, V>
    template <typename K, typename V>
    inline MessageBuilder &operator<<(const QMultiMap<K, V> &map_);
    // QHash<K, V>
    template <typename K, typename V>
    MessageBuilder &operator<<(const QHash<K, V> &hash_);
    // QMultiHash<K, V>
    template <typename K, typename V>
    inline MessageBuilder &operator<<(const QMultiHash<K, V> &multiHash_);

支持WXWIDGETS 相关类型

    #define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem))
    #define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem))
    #define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \
          ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")")

    MAKE_CONTAINERELPP_FRIENDLY 宏定义如下:

    #define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance)                                                                \
        el::base::type::ostream_t &operator<<(el::base::type::ostream_t &ss, const ContainerType &container)                                       \
        {                                                                                                                                          \
            const el::base::type::char_t *sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? ELPP_LITERAL("\n    ") : ELPP_LITERAL(", "); \
            ContainerType::const_iterator elem = container.begin();                                                                                \
            ContainerType::const_iterator endElem = container.end();                                                                               \
            std::size_t size_ = container.SizeMethod;                                                                                              \
            ss << ELPP_LITERAL("[");                                                                                                               \
            for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem)                                     \
            {                                                                                                                                      \
                ss << ElementInstance;                                                                                                             \
                ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));                                                                                  \
            }                                                                                                                                      \
            if (elem != endElem)                                                                                                                   \
            {                                                                                                                                      \
                ss << ELPP_LITERAL("...");                                                                                                         \
            }                                                                                                                                      \
            ss << ELPP_LITERAL("]");                                                                                                               \
            return ss;                                                                                                                             \
        }

    实现也不复杂,就是遍历容器对应的迭代器,依次保存在 ss 流中。
    之所以这里还需要 ElementInstance 这个宏参数,从上面的使用可以看到,WXWIDGETS 相关容器类型的元素值的获取方式是有区别的,有的是一个解引用,有的是两次解引用,还有的希望定制化输出对应元素的值,所以这里提供了出来一个宏参数,让使用者来决定如何针对容器元素定制化输出。

其他类型的支持

    template <class Class>
    ELPP_SIMPLE_LOG(const Class &)

    上面这个宏展开后为:

    template <class Class>
    MessageBuilder &operator<<(const Class &msg)
    {
        m_logger->stream() << msg; // 需要Class类型支持<<运算符
        if (ELPP->hasFlag(LoggingFlag::AutoSpacing))
        {
            m_logger->stream() << " ";
        }
        return *this;
    }

el::base::DispatchAction枚举类型

    enum class DispatchAction : base::type::EnumType
    {
        None = 1,
        NormalLog = 2, // 用户日志
        SysLog = 4     // 系统 syslog 日志(需要系统支持 syslog 日志)
    };

CLOG 宏的实现到这里就介绍完了,下一篇我们继续介绍其他日志记录宏的实现。

标签:std,CLOG,LOG,ITERATOR,++,源码,template,日志,ELPP
From: https://www.cnblogs.com/DesignLife/p/16928408.html

相关文章

  • springboot事务源码分析
    1、本次使用springboot框架分析事务源码2、打开spring-boot-autoconfigure查看spring.factories发现关于事务的自动配置包含:DataSourceTransactionManagerAutoConfigurati......
  • k8s源码分析6-kubectl功能和对象总结
    kubectl的职责主要的工作是处理用户提交的东西(包括,命令行参数,yaml文件等)然后其会把用户提交的这些东西组织成一个数据结构体然后把其发送给APIServerkubectl的代......
  • C++ 使用文件流写/读文本文件、二进制文件、按指定格式写/读文本文件
    1.使用文件流写文本文件:#include<iostream>#include<string>#include<fstream>usingnamespacestd;intmain(){stringname;intage;ofs......
  • 实验三·bdev原理和源码分析
    任务配置bdev运行环境运行hello_bdev程序并分析源码通过bdev接口写入数据并读取Bdev是在物理设备上的进一步抽象,物理层有NVM、NVMeOF、CEPHRBD等,通过bdev向......
  • 实验四·blobstore原理和源码分析
    实验任务学习Blob基本原理完成hello_blob程序运行修改底层bdev为nvmeBlob构建在bdev之上,是对数据存储的进一步抽象,类似于文件,但是不具备文件POSIX接口,可近似按文件形......
  • c++ 面向对象 class类总结
    c++三大特性访问权限​ 在c++中通过public、protected、private三个关键字来控制成员变量和成员函数的访问权限,它们分别表示为公有的、受保护的、私有的,称为成员访问限......
  • easylogging++的那些事(四)源码分析(二)日志记录宏(一)CLOG宏(四)日志信息保存
    目录writer类的输出运算符writer类的流操控符el::base::MessageBuilder类CLOG宏接口调用流程图在上一篇中我们分析完了CLOG宏日志输出的流程,在结尾的时候我们提......
  • 在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++ auto it 遍历改值的坑
    for(autoit:arr)利用这样遍历修改容器的值,是不可以成功修改的。#include<iostream>#include<vector>usingnamespacestd;vector<int>arr;voidshow1(){f......