spdlog 基本結構分析
代碼取自 V1.5.0, 代碼堪稱美學。
spdlog 是一個只有頭文件的C++日志庫,速度非常快,擴展性很強,更重要的是 社區活躍,文檔齊全。
使用
- 參考官方的 example.
- {fmt} 的格式使用也需要熟悉一下,畢竟也進 C++20 了,什么垃圾流就快掃進歷史的垃圾堆吧.
分析
這里選擇了三個點來做分析:
- 提供的 日志格式 非常豐富,並且允許用戶自定義需要的格式。
- 對日志文件的類型也做了充分擴展,支持控制台,普通文件,按大小滾動文件,按時間滾動文件,如果不能滿足需要,可以自己擴展格式,見 spdlog/sinks/base_sink.h。
- 支持單/多線程,異步/同步,阻塞非阻塞模式。
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 總體而言提供了日志接口
spdlog::debug()
, 默認的日志對象,使用默認的日志信息格式,輸出至 stdout。logger->debug()
, 指定日志對象進行日志記錄,輸出至該日志對象對應的文件中。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
管理所有的日志對象
- 使用
<name, logger>
將日志對象和其名稱對應起來,后面使用的時候可以直接通過名稱獲取對應的日志對象。 - 保存全部日志對象的默認屬性,可使用提供的屬性控制接口改變所有的日志對象屬性。
- 提供定時flush,spdlog 的文件操作具有緩沖區屬性,為試日志信息及時落地,后台新生成一個flush線程,設置一個時間(單位:秒)定時喚醒一次進行 flush。
參考
- {fmt}, A modern formatting library. 現代化的格式化庫,速度快,使用簡單,已經確定進入 C++20.