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.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM