C++ spdlog日志庫示例詳解
一、spdlog日志庫詳解
C++的 spdlog 日志庫。這是一款高性能、功能豐富且易于使用的開源日志庫。
1. 概述與核心優(yōu)勢
- 高性能: 這是
spdlog的核心設(shè)計目標之一。它采用了異步日志記錄機制(可選),將日志消息的格式化、寫入等耗時操作放入后臺線程執(zhí)行,極大地減少了對主線程性能的影響。其性能通常優(yōu)于許多其他C++日志庫。 - 極簡的頭部引用: 只需包含一個頭文件
#include <spdlog/spdlog.h>即可開始使用基本功能。 - 豐富的特性:
- 支持多種日志級別(trace, debug, info, warn, error, critical)。
- 支持多種日志目標(控制臺、文件、系統(tǒng)日志、TCP套接字、UDP套接字、Rotating files - 滾動文件、Daily files - 每日文件、Windows debugger 等)。
- 支持自定義格式(使用類似
printf的格式化語法或自定義模式)。 - 支持多線程安全。
- 支持異步日志(默認是同步的,但可以輕松配置為異步)。
- 支持日志刷新策略。
- 支持自定義日志器(logger)和接收器(sink)。
- 支持全局和特定日志器。
- 易用性: API設(shè)計簡潔直觀,學(xué)習(xí)曲線平緩。
2. 核心概念
- 日志器(Logger): 日志記錄的核心對象。每個日志器可以有:
- 名稱: 用于標識和檢索。
- 日志級別: 決定哪些級別的消息會被實際記錄。低于設(shè)置級別的消息會被忽略。
- 接收器(Sinks)列表: 一個或多個輸出目標。日志消息會被發(fā)送到所有關(guān)聯(lián)的接收器。
- 錯誤處理器: 當寫入失敗時的回調(diào)函數(shù)。
- 格式化器: 控制日志消息的輸出格式。
- 接收器(Sink): 負責將格式化后的日志消息輸出到具體的目標。
spdlog提供了多種內(nèi)置接收器:stdout_sink_mt,stderr_sink_mt: 輸出到標準輸出/標準錯誤(多線程安全)。basic_file_sink_mt: 輸出到文件(多線程安全)。rotating_file_sink_mt: 基于文件大小的滾動日志文件。daily_file_sink_mt: 基于時間的每日日志文件(每天或指定時間創(chuàng)建新文件)。syslog_sink_mt: 輸出到Unix系統(tǒng)日志(如/var/log/syslog)。win_eventlog_sink_mt: 輸出到Windows事件查看器。msvc_sink_mt: 輸出到Visual Studio調(diào)試窗口(僅Windows)。tcp_sink_mt,udp_sink_mt: 輸出到網(wǎng)絡(luò)套接字。dist_sink_mt: 分布式接收器,將消息分發(fā)到多個其他接收器。null_sink_mt: 丟棄所有日志消息(用于測試或禁用)。ostream_sink_mt: 輸出到任意std::ostream。ansicolor_sink: 支持ANSI顏色代碼的控制臺輸出。android_sink: Android平臺日志輸出。
- 格式化器(Formatter): 定義了每條日志消息的最終輸出格式。它使用特定的模式字符串來指定如何組合時間戳、日志級別、日志器名稱、線程ID、消息內(nèi)容等。
spdlog默認使用類似以下格式:對應(yīng):[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v
[日期 時間] [級別] [線程ID] 消息正文
你可以使用set_pattern自定義格式。 - 日志級別: 定義了日志消息的嚴重性,從低到高依次為:
spdlog::level::tracespdlog::level::debugspdlog::level::infospdlog::level::warnspdlog::level::errspdlog::level::criticalspdlog::level::off(關(guān)閉所有日志)
每個日志器都有自己的級別,低于該級別的消息不會被記錄。全局默認級別可以通過spdlog::set_level設(shè)置。
3. 基本用法
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h> // 文件接收器
#include <spdlog/sinks/stdout_color_sinks.h> // 帶顏色的控制臺接收器
int main() {
// 創(chuàng)建一個同時輸出到控制臺(帶顏色)和文件的日志器
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/basic-log.txt", true); // true 表示覆蓋而非追加
std::vector<spdlog::sink_ptr> sinks{console_sink, file_sink};
auto logger = std::make_shared<spdlog::logger>("my_logger", sinks.begin(), sinks.end());
// 設(shè)置日志級別 (全局設(shè)置也可以使用 spdlog::set_level)
logger->set_level(spdlog::level::debug);
// 設(shè)置輸出格式 (可選,這里設(shè)置一個簡潔格式)
logger->set_pattern("[%H:%M:%S] [%l] %v");
// 注冊此日志器,之后可通過名稱獲取 (可選)
spdlog::register_logger(logger);
// 使用默認全局日志器 (快速開始)
spdlog::info("This is an info message using the default global logger.");
spdlog::warn("This is a warning message.");
// 使用我們創(chuàng)建的日志器
logger->debug("This is a debug message.");
logger->error("Some error occurred: {}", 42); // 格式化,類似 `printf`
logger->critical("Critical error! Exiting...");
// 刷新日志 (確保所有消息都寫入目標,特別是對于文件。異步日志器通常自動處理)
logger->flush();
// 通過名稱獲取已注冊的日志器
auto same_logger = spdlog::get("my_logger");
same_logger->info("Retrieved logger by name.");
return 0;
}
4. 高級用法與特性
異步日志: 創(chuàng)建異步日志器可以顯著提升性能,尤其在高頻日志場景。
#include <spdlog/async.h>
#include <spdlog/sinks/rotating_file_sink.h>
int main() {
// 創(chuàng)建一個異步日志器工廠 (通常全局一個足夠)
auto async_factory = spdlog::create_async<my_logger_name>("async_logger");
// 添加接收器 (例如一個滾動文件接收器)
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("logs/rotating-log.txt", 1024 * 1024 * 5, 3); // 5MB大小,保留3個備份
async_factory->add_sink(rotating_sink);
// 設(shè)置級別、格式等
async_factory->set_level(spdlog::level::info);
async_factory->set_pattern("%^[%L]%$ %v"); // %^ %$ 用于顏色控制
// 獲取并使用異步日志器
auto async_logger = spdlog::get("async_logger");
async_logger->info("This message is logged asynchronously.");
// 結(jié)束時,確保關(guān)閉異步日志器 (釋放后臺線程)
spdlog::drop_all(); // 或者單獨 drop("async_logger")
return 0;
}- 自定義格式化: 使用
set_pattern方法。模式字符含義:%v- 實際消息文本。%t- 線程ID。%P- 進程ID。%n- 日志器名稱。%l- 日志級別 (短字符:T, D, I, W, E, C)。%L- 日志級別 (長字符:TRACE, DEBUG, INFO, WARN, ERROR, CRITICAL)。%a- 縮寫的星期幾。%A- 完整的星期幾。%b,%h- 縮寫的月份。%B- 完整的月份。%c- 日期時間 (e.g., Thu Aug 23 15:35:46 2018)。%C- 年份 (2位數(shù))。%Y- 年份 (4位數(shù))。%D,%x- 日期 (e.g., 08/23/18)。%T,%X- 時間 (e.g., 15:35:46)。%r- 12小時制時間。%R- 24小時制時間 (HH:MM)。%m- 月份 (數(shù)字)。%d- 日 (數(shù)字)。%H- 小時 (24小時制)。%I- 小時 (12小時制)。%M- 分鐘。%S- 秒。%e- 毫秒。%f- 微秒。%F- 納秒。%p- AM/PM。%z- UTC偏移 (e.g., +0200)。%Z- 時區(qū)名稱。%s- 自紀元以來的秒數(shù)。%g- 自紀元以來的毫秒數(shù)。%u- 自紀元以來的微秒數(shù)。%w- 星期幾 (數(shù)字,0=周日)。%j- 一年中的第幾天。%%- 百分號本身。%^- 開始顏色范圍。%$- 結(jié)束顏色范圍。
日志宏: spdlog 提供了方便的宏,可以自動記錄 __FILE__, __LINE__, __func__ 等信息。
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h> // 對于自定義類型輸出可能需要
// 基本宏
LOG_TRACE(...);
LOG_DEBUG(...);
LOG_INFO(...);
LOG_WARN(...);
LOG_ERROR(...);
LOG_CRITICAL(...);
// 帶位置信息的宏 (文件名、行號、函數(shù)名)
SPDLOG_TRACE(...);
SPDLOG_DEBUG(...);
SPDLOG_INFO(...);
SPDLOG_WARN(...);
SPDLOG_ERROR(...);
SPDLOG_CRITICAL(...);
// 示例
int value = 42;
SPDLOG_INFO("The value is {}", value);
SPDLOG_ERROR("Error in function {} at line {}", __func__, __LINE__);- 自定義接收器: 你可以繼承
spdlog::sinks::base_sink類并實現(xiàn)其sink_it和flush方法,將日志輸出到任何你想要的地方(如數(shù)據(jù)庫、消息隊列等)。 - 錯誤處理: 可以設(shè)置
logger->set_error_handler([](const std::string &msg) { ... })來處理寫入失敗等錯誤。 - 自動注冊與獲?。?/strong> 使用
spdlog::create或spdlog::create_async創(chuàng)建的日志器會自動注冊到全局注冊表。之后可以通過spdlog::get("name")獲取。使用spdlog::drop_all()或spdlog::drop("name")釋放資源(特別是異步日志器的后臺線程)。
5. 性能考量與最佳實踐
- 評估是否需要異步: 對于性能要求極高的應(yīng)用,或者日志量非常大的場景,使用異步日志器。但異步日志器在程序異常退出時可能會丟失最后幾條日志(雖然可以通過設(shè)置隊列大小和刷新策略緩解)。
- 合理設(shè)置日志級別: 生產(chǎn)環(huán)境通常設(shè)置為
info或warn,開發(fā)調(diào)試時可用debug或trace。避免在循環(huán)中記錄大量低級別日志。 - 選擇合適的接收器: 文件寫入通常比控制臺慢。
rotating_file_sink和daily_file_sink有助于管理日志文件大小和數(shù)量。 - 避免昂貴的操作: 在日志語句中盡量避免執(zhí)行耗時的操作(如復(fù)雜計算、IO操作),尤其是在同步日志模式下。可以先判斷日志級別是否啟用
if (logger->should_log(spdlog::level::debug)) { ... }。 - 注意格式化開銷: 復(fù)雜的格式化字符串或大量參數(shù)也會帶來開銷。
6. 編譯與依賴
- 依賴:
spdlog是一個頭文件庫(header-only),但需要依賴一個C++11兼容的編譯器和一個格式化庫(默認使用其內(nèi)置的fmt庫分支)。因此,通常只需要包含頭文件即可使用。 - 編譯選項: 可以通過定義宏來控制
spdlog的行為:SPDLOG_COMPILED_LIB: 將其編譯為靜態(tài)庫(需要包含spdlog.cpp)。SPDLOG_FMT_EXTERNAL: 使用外部的fmt庫(需確保已安裝)而不是spdlog內(nèi)置的版本。SPDLOG_NO_EXCEPTIONS: 禁用異常(在異常被禁用的環(huán)境中使用)。SPDLOG_NO_THREAD_ID: 禁用線程ID記錄(如果獲取線程ID開銷大)。SPDLOG_CLOCK_COARSE: 使用更快的coarse時鐘源(精度較低)。
- 安裝: 可以通過包管理器(如vcpkg, conan)、Git子模塊或直接下載源碼放入項目目錄。
7. 總結(jié)
spdlog 是一個強大、高效且靈活的C++日志庫,幾乎能滿足所有常見的日志記錄需求。其簡潔的API、出色的性能表現(xiàn)(尤其是異步模式)以及豐富的功能(多接收器、自定義格式、多級別)使其成為C++項目中日志組件的優(yōu)秀選擇。無論是快速原型開發(fā)還是大型高性能應(yīng)用,spdlog 都值得考慮。
二、庫的下載與編譯
參考我這篇博文:C++ spdlog日志庫編譯與安裝詳解
三、示例
1、測試代碼
以下是一個較為復(fù)雜的C++ spdlog日志庫使用示例,包含多線程、異步日志、多接收器(sink)和自定義格式設(shè)置:
#include <spdlog/spdlog.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/async.h>
#include <thread>
#include <iostream>
int main() {
try {
// 創(chuàng)建異步日志線程池(隊列大小1024,線程數(shù)1)
spdlog::init_thread_pool(1024, 1);
// 創(chuàng)建文件接收器(每天輪轉(zhuǎn),最大3個文件,單個文件最大50MB)
auto file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
"logs/mylog.log", 50 * 1024 * 1024, 3
);
file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v");
// 創(chuàng)建控制臺彩色輸出接收器
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] %v");
// 創(chuàng)建異步日志器(組合兩個接收器)
std::vector<spdlog::sink_ptr> sinks{file_sink, console_sink};
auto async_logger = std::make_shared<spdlog::async_logger>(
"async_logger", sinks.begin(), sinks.end(), spdlog::thread_pool()
);
async_logger->set_level(spdlog::level::trace); // 設(shè)置日志級別
// 注冊全局日志器
spdlog::register_logger(async_logger);
spdlog::set_default_logger(async_logger);
// 自定義錯誤處理
spdlog::flush_every(std::chrono::seconds(3)); // 每3秒刷新到磁盤
spdlog::set_error_handler([](const std::string& msg) {
std::cerr << "Log error: " << msg << std::endl;
});
// 多線程日志測試
auto log_func = []() {
for (int i = 0; i < 100; ++i) {
SPDLOG_TRACE("Trace message {}", i);
SPDLOG_DEBUG("Debug message {}", i);
SPDLOG_INFO("Info message {}", i);
SPDLOG_WARN("Warning message {}", i);
SPDLOG_ERROR("Error message {}", i);
SPDLOG_CRITICAL("Critical message {}", i);
}
};
std::thread t1(log_func);
std::thread t2(log_func);
t1.join();
t2.join();
// 顯式刷新確保所有日志寫入
spdlog::shutdown();
} catch (const spdlog::spdlog_ex& ex) {
std::cerr << "Log exception: " << ex.what() << std::endl;
}
return 0;
}2、運行結(jié)果



3、功能說明:
- 多接收器配置:
- 文件接收器:帶輪轉(zhuǎn)功能(每天/按大?。?/li>
- 控制臺接收器:帶彩色級別顯示
- 異步日志:
- 使用線程池處理日志寫入
- 避免阻塞主線程
- 自定義格式:
- 時間格式:
%Y-%m-%d %H:%M:%S.%e - 包含線程ID:
%t - 級別高亮:
%^%l%$
- 時間格式:
- 多線程支持:
- 兩個線程同時寫入日志
- 線程安全保證
- 異常處理:
- 自定義錯誤回調(diào)
- 全局異常捕獲
- 性能優(yōu)化:
- 定期刷新(
flush_every) - 隊列大小控制
- 定期刷新(
到此這篇關(guān)于C++ spdlog日志庫詳解的文章就介紹到這了,更多相關(guān)C++ spdlog日志庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解C++內(nèi)存的代碼區(qū),全局區(qū),棧區(qū)和堆區(qū)
這篇文章主要為大家介紹了C++內(nèi)存的代碼區(qū),全局區(qū),棧區(qū)和堆區(qū),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2021-12-12

