• spdlog 基本结构分析


    spdlog 基本结构分析

    代码取自 V1.5.0, 代码堪称美学。

    spdlog 是一个只有头文件的C++日志库,速度非常快,扩展性很强,更重要的是 社区活跃,文档齐全

    使用

    1. 参考官方的 example.
    2. {fmt} 的格式使用也需要熟悉一下,毕竟也进 C++20 了,什么垃圾流就快扫进历史的垃圾堆吧.

    分析

    这里选择了三个点来做分析:

    1. 提供的 日志格式 非常丰富,并且允许用户自定义需要的格式。
    2. 对日志文件的类型也做了充分扩展,支持控制台,普通文件,按大小滚动文件,按时间滚动文件,如果不能满足需要,可以自己扩展格式,见 spdlog/sinks/base_sink.h
    3. 支持单/多线程,异步/同步,阻塞非阻塞模式。

    spdlog 的代码结构如下:

    spdlog
        ├─example  用法代码
        ├─include  实现目录
        │  └─spdlog
        │      ├─details  功能函数目录
        │      ├─fmt  {fmt} 库目录
        │      ├─sinks  落地文件格式实现
        │      └─*.h    异步模式,日志库接口等实现
        ├─src  .cpp 文件,组成编译模块生成静态库使用
        ├─test  测试代码
    

    基本逻辑结构如下:

    有几个比较重要的文件:

    • spdlog/spdlog.h 为日志库接口,提供日志宏的属性控制函数。
    • spdlog/logger.h 为日志管理器,为前后端连接的枢纽。
    • spdlog/async.h 为异步模式接口。
    • spdlog/sinks/base_sink.h 为日志文件格式父类,后面所有的日志文件格式都是继承该类来实现不同功能。
    • spdlog/sinks/registry.h 用于登记所有的logger,及一些默认的属性,如日志格式、日志写入等级。

    spdlog 接口

    spdlog 总体而言提供了日志接口

    1. spdlog::debug(), 默认的日志对象,使用默认的日志信息格式,输出至 stdout。
    2. logger->debug(), 指定日志对象进行日志记录,输出至该日志对象对应的文件中。
    3. SPDLOG_LOGGER_DEBUG(logger), SPDLOG_DEBUG(), 使用宏对以上两种接口进行包装,产生的日志格式包含 文件、函数、行。

    提供的一些落地的文件类型:

    • 标准输出
    • 带颜色的标准输出(默认)
    • 基本文件
    • 可设定时间的滚动文件
    • 可设定大小的滚动文件
    • 过滤重复的日志
    • syslog 日志

    这里简单提一下 sinks 的实现,所有落地文件的类型都是从 base_sink(忽略sink) 继承而来,提供了两个纯虚函数 sink_it_()flush_(),这样一来,需要扩展的类型文件只需要实现两个函数,很大的简化了扩展的流程。而单/多线程通过模板来确定是否需要使用互斥量来保持同步,也算是比较有意思的一个实现了:

    // 例子使用 base_file_sink
    using basic_file_sink_mt = basic_file_sink<std::mutex>;
    using basic_file_sink_st = basic_file_sink<details::null_mutex>;
    
    struct null_mutex
    {
        void lock() const {}
        void unlock() const {}
        bool try_lock() const
        {
            return true;
        }
    };
    
    template<typename Mutex>
    void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
    {
        std::lock_guard<Mutex> lock(mutex_);
        sink_it_(msg);
    }
    

    如果需要使用多线程模式,就使用 std::mutex,如果是单线程,我们就不需要互斥来同步,这里实现一个空的互斥量类,就使得 log() 的代码完全不需要修改了,也提高了内聚性。

    spdlog 默认使用同步模式,也可以设置异步模式,异步模式会创建一个线程池,线程池大小可以自行设置,默认为1,该线程池所有者为 details::registry::instance(). 后台的大小可以设置的 多生产者多消费者队列 默认为阻塞模式,也可以设置为非阻塞,不过这个非阻塞的处理非常简单粗暴,就是简单的丢弃最老的日志,推荐是不要这样设置滴,一般产生阻塞的情况大概是磁盘IO打满了,出现这个情况一般是别的地方出问题了。

    details::registry 管理所有的日志对象

    1. 使用 <name, logger> 将日志对象和其名称对应起来,后面使用的时候可以直接通过名称获取对应的日志对象。
    2. 保存全部日志对象的默认属性,可使用提供的属性控制接口改变所有的日志对象属性。
    3. 提供定时flush,spdlog 的文件操作具有缓冲区属性,为试日志信息及时落地,后台新生成一个flush线程,设置一个时间(单位:秒)定时唤醒一次进行 flush。

    参考

    1. {fmt}, A modern formatting library. 现代化的格式化库,速度快,使用简单,已经确定进入 C++20.
  • 相关阅读:
    如何使不同时区的时间与京8区一致?(JS实现)
    再论递归
    如何实现keep-alive
    [小tips]使用vscode,根据vue模板文件生成代码
    gulp-load-task 解决 gulpfile.js 过大的问题
    《Vue 编程房内考》
    前端开发笔试题汇总
    HTTP状态码及其含义 503 500 401 200 301 302
    IE, FF, Safari前端开发常用调试工具
    DOCTYPE与浏览器模式详解(标准模式&混杂模式)
  • 原文地址:https://www.cnblogs.com/shuqin/p/12214439.html
Copyright © 2020-2023  润新知