spdlog 完全指南:C++ 高性能日志库深度解析
适用版本 :spdlog v1.x(主流版本)依赖 :C++11 及以上,header-only 或编译库模式GitHub :https://github.com/gabime/spdlog
目录
什么是 spdlog
安装与集成
核心概念
快速上手
日志级别详解
Sink(输出目标)详解
Logger 的创建与管理
格式化(Format)系统
异步日志(Async Logger)
日志轮转(Rotating / Daily)
多线程安全
自定义 Sink
性能调优
常见使用模式
与 CMake 集成
常见问题与踩坑
总结对比
1. 什么是 spdlog spdlog 是一个 **C++ 高性能、零开销(zero overhead)**的日志库,由 Gabi Melman 开发并维护。它是目前 C++ 社区最流行的日志库之一,广泛用于游戏开发、后台服务、嵌入式系统等领域。
核心优势
特性
说明
Header-only
可选纯头文件模式,无需额外编译
高性能
异步模式下可达百万级 msg/s
零开销宏
关闭日志级别时,编译期直接消除,无运行时损耗
fmt 集成
使用 fmtlib(或内置 fmt)做格式化,支持 {} 风格
多 Sink
同一 logger 可同时输出到控制台、文件、网络等
线程安全
提供线程安全和非线程安全两种 logger
丰富的 Sink
内置轮转文件、每日文件、syslog、Android log 等
全局注册表
可通过名字全局访问 logger
架构概览 1 2 3 4 5 6 7 8 9 10 11 12 用户代码 │ ▼ Logger ←── 持有一个或多个 Sink │ │ │ ├── stdout_color_sink (控制台彩色输出) │ ├── basic_file_sink (基础文件) │ ├── rotating_file_sink (轮转文件) │ ├── daily_file_sink (每日文件) │ └── custom_sink (自定义) │ Formatter ──── 决定日志行的格式(时间、线程、级别、消息)
2. 安装与集成 1 2 3 4 5 git clone https://github.com/gabime/spdlog.gitcp -r spdlog/include/spdlog /your/project/include/
在代码中引入:
1 #include "spdlog/spdlog.h"
注意 :header-only 模式每个编译单元都会实例化模板,编译速度较慢,适合小项目。
方式二:编译库模式(推荐用于大型项目) 1 2 3 4 5 mkdir build && cd build cmake .. -DSPDLOG_BUILD_SHARED=ON cmake .. -DSPDLOG_BUILD_SHARED=OFF make && sudo make install
代码中需要在包含前定义宏:
1 2 3 #define SPDLOG_COMPILED_LIB #include "spdlog/spdlog.h"
方式三:vcpkg
方式四:Conan 1 2 3 [requires] spdlog/1.12.0
3. 核心概念 理解 spdlog 的三个核心概念非常重要:Logger 、Sink 、Formatter 。
Logger Logger 是用户直接交互的对象,负责:
接收日志消息
与自身的日志级别做比较(过滤)
将通过过滤的消息转发给所有 Sink
1 2 3 4 5 auto logger = std::make_shared <spdlog::logger>("my_logger" , sink);
Sink Sink 是实际执行输出的组件,是日志的”目的地”。spdlog 内置了十几种 Sink,也可以自定义。每个 Sink 也有自己独立的日志级别(进行二次过滤)。
1 2 3 Logger level = warn → 只有 warn 及以上的消息才传给 Sink ↓ Sink level = error → 只有 error 及以上才真正写出
Formatter 决定日志消息的格式,支持自定义 pattern。
4. 快速上手 最简示例 1 2 3 4 5 6 7 8 9 #include "spdlog/spdlog.h" int main () { spdlog::info ("Hello, spdlog! value={}" , 42 ); spdlog::warn ("This is a warning: {:.2f}" , 3.14159 ); spdlog::error ("Error code: {}" , -1 ); return 0 ; }
输出:
1 2 3 [2024-01-15 10:30:00.123] [info ] Hello, spdlog! value =42 [2024-01-15 10:30:00.123] [warning ] This is a warning: 3.14 [2024-01-15 10:30:00.123] [error ] Error code: -1
创建文件 Logger 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include "spdlog/spdlog.h" #include "spdlog/sinks/basic_file_sink.h" int main () { auto logger = spdlog::basic_logger_mt ("file_logger" , "logs/app.log" ); logger->info ("Application started" ); logger->debug ("Debug message: {}" , 100 ); logger->error ("Something went wrong!" ); logger->flush (); return 0 ; }
格式化示例(fmt 风格) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include "spdlog/spdlog.h" struct Point { int x, y; };template <>struct fmt ::formatter<Point> { constexpr auto parse (format_parse_context& ctx) { return ctx.begin (); } template <typename FormatContext> auto format (const Point& p, FormatContext& ctx) { return fmt::format_to(ctx.out (), "({}, {})" , p.x, p.y); } };int main () { Point pt{3 , 5 }; spdlog::info ("Point: {}" , pt); spdlog::info ("Hex: {:#010x}" , 255 ); spdlog::info ("Aligned: {:>10}" , "right" ); spdlog::info ("Vec: {:02d}" , 7 ); }
5. 日志级别详解 spdlog 定义了 7 个日志级别(从低到高):
1 2 3 4 5 6 7 8 9 namespace spdlog::level { trace = 0 , debug = 1 , info = 2 , warn = 3 , err = 4 , critical = 5 , off = 6 , }
设置日志级别 1 2 3 4 5 6 7 8 9 10 11 12 spdlog::set_level (spdlog::level::debug);auto logger = spdlog::get ("my_logger" ); logger->set_level (spdlog::level::warn);if (logger->should_log (spdlog::level::debug)) { logger->debug ("expensive: {}" , compute_string ()); }
编译期级别裁剪(零开销) 通过宏在编译期消除低级别日志,完全无运行时开销:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 SPDLOG_TRACE ("This will be compiled away if level > TRACE" );SPDLOG_DEBUG ("This will be compiled away if level > DEBUG" );SPDLOG_INFO ("Always present in this example" );SPDLOG_WARN ("Warning: {}" , msg);SPDLOG_ERROR ("Error: {}" , err);SPDLOG_CRITICAL ("Critical: {}" , detail);SPDLOG_LOGGER_DEBUG (logger, "debug msg: {}" , val);SPDLOG_LOGGER_INFO (logger, "info msg" );
CMakeLists.txt 配置:
1 2 target_compile_definitions (MyApp PRIVATE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO)
6. Sink(输出目标)详解 6.1 控制台 Sink 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include "spdlog/sinks/stdout_color_sinks.h" auto console = spdlog::stdout_color_mt ("console" );auto console_st = spdlog::stdout_color_st ("console_st" );auto err_logger = spdlog::stderr_color_mt ("stderr" ); console->info ("Colored info message" ); console->warn ("Colored warn message" ); console->error ("Colored error message" );
颜色对应关系:
级别
颜色
trace
白色
debug
青色
info
绿色
warn
黄色(加粗)
error
红色(加粗)
critical
白底红字
6.2 基础文件 Sink 1 2 3 4 5 6 7 #include "spdlog/sinks/basic_file_sink.h" auto logger = spdlog::basic_logger_mt ("basic" , "logs/app.log" );auto logger_trunc = spdlog::basic_logger_mt ("basic" , "logs/app.log" , true );
6.3 轮转文件 Sink(Rotating) 按文件大小轮转,适合长期运行的服务:
1 2 3 4 5 6 7 8 9 #include "spdlog/sinks/rotating_file_sink.h" auto logger = spdlog::rotating_logger_mt ( "rotating" , "logs/app.log" , 1024 * 1024 * 10 , 5 );
轮转规则:
1 2 3 4 5 app .log ← 当前写入app .1.log ← 上一个app .2.log ← 更早app .3.log app .4.log ← 最旧(超过后删除)
6.4 每日文件 Sink(Daily) 每天在指定时间创建新文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 #include "spdlog/sinks/daily_file_sink.h" auto logger = spdlog::daily_logger_mt ( "daily" , "logs/app.log" , 0 , 0 );auto logger7 = spdlog::daily_logger_mt ("daily" , "logs/app.log" , 0 , 0 , false , 7 );
6.5 系统日志 Sink(Linux syslog) 1 2 3 4 #include "spdlog/sinks/syslog_sink.h" auto syslog_logger = spdlog::syslog_logger_mt ("syslog" , "my_app" , LOG_PID); syslog_logger->info ("This goes to /var/log/syslog" );
6.6 多 Sink 组合(Distributed Logger) 这是 spdlog 最强大的功能之一:同一个 logger 同时输出到多个目标:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/basic_file_sink.h" void setup_logger () { auto console_sink = std::make_shared <spdlog::sinks::stdout_color_sink_mt>(); console_sink->set_level (spdlog::level::warn); console_sink->set_pattern ("[%H:%M:%S] [%^%l%$] %v" ); auto file_sink = std::make_shared <spdlog::sinks::rotating_file_sink_mt>( "logs/app.log" , 1024 * 1024 * 5 , 3 ); file_sink->set_level (spdlog::level::trace); spdlog::logger logger ("multi_sink" , {console_sink, file_sink}) ; logger.set_level (spdlog::level::trace); spdlog::register_logger (std::make_shared <spdlog::logger>(logger)); }int main () { setup_logger (); auto logger = spdlog::get ("multi_sink" ); logger->trace ("Trace: 控制台看不到,文件能看到" ); logger->warn ("Warn: 控制台和文件都能看到" ); logger->error ("Error: 两处都显示" ); }
7. Logger 的创建与管理 7.1 全局注册表 spdlog 维护一个全局的 logger 注册表,可以跨文件访问:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 auto logger = spdlog::basic_logger_mt ("app" , "logs/app.log" );auto logger = spdlog::get ("app" );if (logger) { logger->info ("Got it!" ); }auto my_logger = std::make_shared <spdlog::logger>("manual" , sink); spdlog::register_logger (my_logger); spdlog::drop ("app" ); spdlog::drop_all ();
7.2 默认 Logger 1 2 3 4 5 6 7 8 auto default_logger = spdlog::default_logger (); spdlog::set_default_logger (spdlog::basic_logger_mt ("new_default" , "logs/default.log" )); spdlog::info ("This goes to file now" );
7.3 Logger 克隆 1 2 3 4 auto cloned = default_logger->clone ("cloned_logger" ); cloned->set_level (spdlog::level::err); spdlog::register_logger (cloned);
8.1 Pattern 标志 通过 set_pattern 自定义格式:
1 spdlog::set_pattern ("[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] [%t] %v" );
完整的 Pattern 标志表:
标志
含义
示例
%v
日志消息内容
Hello World
%t
线程 ID
12345
%P
进程 ID
9876
%n
Logger 名称
my_logger
%l
日志级别(小写)
info
%L
日志级别(单字母)
I
%^
颜色范围开始
—
%$
颜色范围结束
—
%Y
年(4位)
2024
%m
月(2位)
01
%d
日(2位)
15
%H
小时(24时制)
14
%M
分钟
30
%S
秒
05
%e
毫秒
123
%f
微秒
123456
%F
纳秒
123456789
%E
Unix 时间戳(秒)
1705300200
%s
源文件名(需宏)
main.cpp
%#
源文件行号(需宏)
42
%!
函数名(需宏)
main
%@
文件名:行号
main.cpp:42
8.2 常用格式示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 spdlog::set_pattern ("%H:%M:%S [%l] %v" ); spdlog::set_pattern ("%^[%H:%M:%S] [%l]%$ %v" ); spdlog::set_pattern ("[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%-8l%$] [tid:%t] %v" ); spdlog::set_pattern ("[%l] [%s:%#] %v" );
8.3 自定义格式化 Flag 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class my_flag_formatter : public spdlog::custom_flag_formatter {public : void format (const spdlog::details::log_msg&, const std::tm&, spdlog::memory_buf_t & dest) override { std::string stars (3 , '*' ) ; dest.append (stars.data (), stars.data () + stars.size ()); } std::unique_ptr<custom_flag_formatter> clone () const override { return spdlog::details::make_unique <my_flag_formatter>(); } };auto formatter = std::make_unique <spdlog::pattern_formatter>(); formatter->add_flag <my_flag_formatter>('*' ).set_pattern ("[%*] %v" ); spdlog::set_formatter (std::move (formatter)); spdlog::info ("test" );
9. 异步日志(Async Logger) 异步模式将日志写入操作移到后台线程,大幅降低调用线程的延迟。
9.1 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include "spdlog/async.h" #include "spdlog/sinks/basic_file_sink.h" int main () { spdlog::init_thread_pool (8192 , 1 ); auto async_logger = spdlog::basic_logger_async <spdlog::async_factory>( ); auto async_file = spdlog::basic_logger_mt <spdlog::async_factory>( "async_logger" , "logs/async.log" ); for (int i = 0 ; i < 100000 ; i++) { async_file->info ("Async message #{}" , i); } spdlog::shutdown (); return 0 ; }
9.2 溢出策略 1 2 3 4 5 6 auto logger = spdlog::create_async <spdlog::sinks::stdout_color_sink_mt>("async" );auto logger_overrun = spdlog::create_async_nb <spdlog::sinks::stdout_color_sink_mt>("async_nb" );
9.3 异步原理 1 2 3 4 5 6 7 调用线程 后台线程池 │ │ │ log ( "msg" ) │ │──→ 加入环形队列 ──────────→│ 取出消息 │ ( 无锁 SPSC/MPSC) │ 调用 Sink 写入 │ 立即返回 │ 磁盘/网络 IO │ │
关键参数建议:
1 2 3 4 5 6 7 spdlog::init_thread_pool (8192 , 1 ); spdlog::init_thread_pool (8192 , 2 );
9.4 手动创建异步 Logger 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include "spdlog/async.h" auto tp = std::make_shared <spdlog::details::thread_pool>(8192 , 1 );auto file_sink = std::make_shared <spdlog::sinks::rotating_file_sink_mt>( "logs/async.log" , 1024 *1024 *5 , 3 );auto logger = std::make_shared <spdlog::async_logger>( "async" , file_sink, tp, spdlog::async_overflow_policy::block ); spdlog::register_logger (logger);
10. 日志轮转(Rotating / Daily) 10.1 轮转文件详解 1 2 3 4 5 6 7 8 9 10 11 12 #include "spdlog/sinks/rotating_file_sink.h" auto rotating_sink = std::make_shared <spdlog::sinks::rotating_file_sink_mt>( "logs/app.log" , 1024 * 1024 * 100 , 10 , false ); rotating_sink->rotate_now ();
10.2 每日文件详解 1 2 3 4 5 6 7 8 9 10 11 12 13 #include "spdlog/sinks/daily_file_sink.h" auto daily_sink = std::make_shared <spdlog::sinks::daily_file_sink_mt>( "logs/app.log" , 2 , 30 , false , 30 );
生成的文件名格式:logs/app_2024-01-15.log
11. 多线程安全 spdlog 的所有内置 sink 都有线程安全(_mt)和非线程安全(_st)两个版本:
1 2 3 4 5 auto mt_logger = spdlog::basic_logger_mt ("mt" , "logs/mt.log" );auto st_logger = spdlog::basic_logger_st ("st" , "logs/st.log" );
选择原则 1 2 3 多线程环境 → 使用 _mt 版本 单线程环境 → 使用 _st 版本(性能更好) 异步模式 → 后台线程是单线程,sink 用 _st ;但注意队列本身是线程安全的
多线程示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include <thread> #include "spdlog/spdlog.h" #include "spdlog/sinks/rotating_file_sink.h" void worker (int id, std::shared_ptr<spdlog::logger> logger) { for (int i = 0 ; i < 1000 ; i++) { logger->info ("Thread {} iteration {}" , id, i); } }int main () { auto logger = spdlog::rotating_logger_mt ("mt" , "logs/mt.log" , 1024 *1024 , 3 ); std::vector<std::thread> threads; for (int i = 0 ; i < 10 ; i++) { threads.emplace_back (worker, i, logger); } for (auto & t : threads) t.join (); spdlog::drop_all (); return 0 ; }
12. 自定义 Sink 当内置 Sink 无法满足需求时(如发送到数据库、消息队列、Webhook),可以自定义:
12.1 继承 base_sink 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include "spdlog/sinks/base_sink.h" #include <mutex> #include <vector> template <typename Mutex>class memory_sink : public spdlog::sinks::base_sink<Mutex> {public : std::vector<std::string>& messages () { return messages_; }protected : void sink_it_ (const spdlog::details::log_msg& msg) override { spdlog::memory_buf_t formatted; spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted); messages_.emplace_back (fmt::to_string (formatted)); } void flush_ () override { }private : std::vector<std::string> messages_; };using memory_sink_mt = memory_sink<std::mutex>;using memory_sink_st = memory_sink<spdlog::details::null_mutex>;
12.2 HTTP Sink 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include "spdlog/sinks/base_sink.h" template <typename Mutex>class http_sink : public spdlog::sinks::base_sink<Mutex> {public : explicit http_sink (std::string url) : url_(std::move(url)) { }protected : void sink_it_ (const spdlog::details::log_msg& msg) override { spdlog::memory_buf_t formatted; this ->formatter_->format(msg, formatted); std::string log_str = fmt::to_string (formatted); } void flush_ () override {}private : std::string url_; };using http_sink_mt = http_sink<std::mutex>;
12.3 使用自定义 Sink 1 2 3 4 5 6 7 8 9 10 11 12 13 int main () { auto mem_sink = std::make_shared <memory_sink_mt>(); auto logger = std::make_shared <spdlog::logger>("test" , mem_sink); spdlog::register_logger (logger); logger->info ("test message 1" ); logger->warn ("test message 2" ); for (const auto & msg : mem_sink->messages ()) { std::cout << msg; } }
13. 性能调优 13.1 基准测试数据(官方)
模式
吞吐量
同步(stdout,单线程)
~300 万 msg/s
同步(文件,单线程)
~150 万 msg/s
异步(文件,单线程)
~300 万 msg/s
异步(文件,多线程)
线性扩展
13.2 优化策略 1. 编译期裁剪(最重要)
1 2 3 4 target_compile_definitions (App PRIVATE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO )
2. 使用异步模式
1 2 3 4 spdlog::init_thread_pool (65536 , 1 ); auto logger = spdlog::rotating_logger_mt <spdlog::async_factory>( "async" , "logs/app.log" , 1024 *1024 *100 , 10 );
3. 减少刷新频率
1 2 3 4 5 6 spdlog::flush_every (std::chrono::seconds (3 )); logger->flush_on (spdlog::level::err);
4. 使用 st 版本(单线程场景)
1 2 auto logger = spdlog::basic_logger_st ("fast" , "logs/fast.log" );
5. 避免在热路径上构造临时字符串
1 2 3 4 5 6 7 8 logger->debug ("Result: " + std::to_string (expensive_call ())); logger->debug ("Result: {}" , expensive_call ());SPDLOG_LOGGER_DEBUG (logger, "Result: {}" , expensive_call ());
6. 批量日志时使用 should_log 守卫
1 2 3 4 5 if (logger->should_log (spdlog::level::debug)) { auto report = generate_debug_report (); logger->debug ("{}" , report); }
14. 常见使用模式 14.1 单例 Logger 管理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #pragma once #include "spdlog/spdlog.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/rotating_file_sink.h" class LoggerManager {public : static LoggerManager& instance () { static LoggerManager inst; return inst; } void init (const std::string& log_dir, spdlog::level::level_enum level) { auto console_sink = std::make_shared <spdlog::sinks::stdout_color_sink_mt>(); console_sink->set_level (spdlog::level::warn); auto file_sink = std::make_shared <spdlog::sinks::rotating_file_sink_mt>( log_dir + "/app.log" , 1024 *1024 *50 , 5 ); file_sink->set_level (level); auto logger = std::make_shared <spdlog::logger>( "app" , spdlog::sinks_init_list{console_sink, file_sink} ); logger->set_level (level); logger->set_pattern ("[%Y-%m-%d %H:%M:%S.%e] [%^%-8l%$] [%t] %v" ); spdlog::set_default_logger (logger); spdlog::register_logger (logger); } void shutdown () { spdlog::shutdown (); } private : LoggerManager () = default ; };#define LOG_INFO(...) SPDLOG_INFO(__VA_ARGS__) #define LOG_WARN(...) SPDLOG_WARN(__VA_ARGS__) #define LOG_ERROR(...) SPDLOG_ERROR(__VA_ARGS__) #define LOG_DEBUG(...) SPDLOG_DEBUG(__VA_ARGS__)
14.2 模块化 Logger 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class NetworkModule {public : NetworkModule () { logger_ = spdlog::get ("network" ); if (!logger_) { logger_ = spdlog::basic_logger_mt ("network" , "logs/network.log" ); } } void connect (const std::string& host) { logger_->info ("Connecting to {}" , host); } private : std::shared_ptr<spdlog::logger> logger_; };
14.3 条件日志 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void process_request () { static std::atomic<int > count{0 }; if (++count % 1000 == 0 ) { spdlog::info ("Processed {} requests" , count.load ()); } }void high_freq_operation (int id) { if (id % 100 == 0 ) { spdlog::debug ("Sample: id={}" , id); } }
14.4 结构化日志(JSON 风格) 1 2 3 4 5 spdlog::set_pattern (R"({"time":"%Y-%m-%dT%H:%M:%S.%e","level":"%l","msg":"%v"})" ); spdlog::info ("User login: user_id={} ip={}" , user_id, ip);
15. 与 CMake 集成 15.1 FetchContent(推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 cmake_minimum_required (VERSION 3.14 )project (MyApp)include (FetchContent) FetchContent_Declare( spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG v1.13.0 ) FetchContent_MakeAvailable(spdlog)add_executable (MyApp main.cpp)target_link_libraries (MyApp PRIVATE spdlog::spdlog)target_compile_definitions (MyApp PRIVATE SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG )
15.2 find_package(系统安装) 1 2 find_package (spdlog REQUIRED)target_link_libraries (MyApp PRIVATE spdlog::spdlog)
15.3 add_subdirectory 1 2 add_subdirectory (third_party/spdlog)target_link_libraries (MyApp PRIVATE spdlog::spdlog)
16. 常见问题与踩坑 Q1: 日志不输出怎么办? 1 2 3 4 5 6 7 8 9 10 11 12 13 logger->set_level (spdlog::level::trace); sink->set_level (spdlog::level::trace);#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE SPDLOG_TRACE ("This needs SPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE" ); logger->flush ();
Q2: 程序崩溃时日志丢失? 1 2 3 4 5 6 7 8 9 10 11 logger->flush_on (spdlog::level::err);#include <csignal> void signal_handler (int signal) { spdlog::shutdown (); exit (signal); }signal (SIGSEGV, signal_handler);signal (SIGABRT, signal_handler);
Q3: 异步 logger 程序退出时丢消息? 1 2 3 4 5 6 7 8 int main () { spdlog::shutdown (); return 0 ; }
Q4: 多个翻译单元都引入 spdlog,编译很慢?
Q5: fmt 版本冲突? spdlog 内置了 bundled fmt(位于 include/spdlog/fmt/),如果项目中已有 fmt:
1 2 3 4 find_package (fmt REQUIRED)target_compile_definitions (spdlog INTERFACE SPDLOG_FMT_EXTERNAL)target_link_libraries (spdlog INTERFACE fmt::fmt)
Q6: Windows 中文乱码? 1 2 3 4 5 #ifdef _WIN32 #include <windows.h> SetConsoleOutputCP (CP_UTF8);#endif
Q7: 日志文件权限问题(Linux) 1 2 3 mkdir -p /var/log/myappchown myuser:myuser /var/log/myapp
17. 总结对比 spdlog vs 其他日志库
特性
spdlog
log4cpp
Boost.Log
glog
header-only
✅
❌
❌
❌
异步支持
✅
❌
✅
❌
格式化(fmt)
✅ 原生
❌
❌
❌
性能
⭐⭐⭐⭐⭐
⭐⭐
⭐⭐⭐
⭐⭐⭐
易用性
⭐⭐⭐⭐⭐
⭐⭐⭐
⭐⭐
⭐⭐⭐
依赖
仅 fmt
多
Boost
gflags
C++ 标准
C++11+
C++03+
C++11+
C++11+
适用场景
场景
推荐配置
开发调试
stdout_color_mt + trace 级别
单机后台服务
rotating_file_mt + 异步
高频交易/游戏
async + overrun_oldest 策略
微服务
结构化 JSON 格式 + ELK
嵌入式
st 版本 + 关闭 trace/debug
单元测试
自定义 memory_sink
参考资源