1. 用于啥需求?
打日志。spdlog 是一个高性能、易用的 C++ 日志库。
2. 何时使用 spdlog?
其格式化风格同 std::format
/std::print
,如果这符合您的习惯可以考虑。稣一般会在复杂场景下使用 Boost.Log,测试程序或简单的程序里使用 spdlog。
举例啥叫复杂场景:产品里有多个可执行程序(Executable),譬如说 A 和 B,它们共同使用多个动态链接库(Dynamic-Link Library),譬如说 X 和 Y。注意,这里说的“使用”,可能是静态加载,也可能是动态加载(比如动态链接库是插件)。当 X/Y 在 A 进程里时,它们的打印风格、设置都应该受 A 控制,而在 B 进程时,则受 B 控制。不管可执行程序和动态链接库有多少个,每个进程都应该只有一个 Logger。这个需求 Boost.Log 能轻松实现,而 spdlog 可能不轻松,因为在雪蛤油时,有个熟悉 spdlog 的同学和稣打赌,结果他用没能轻易实现。也就是说即使 spdlog 能实现,那也不轻松。
3. 具体应用
首先注意到 Logger 的打印接口有两大类,一类是函数,比如 spdlog::info
,只要你用了,它就被编译到程序里;另一类是宏,比如说 SPDLOG_LEVEL_INFO
,它是否会被编译到程序里,受 SPDLOG_ACTIVE_LEVEL
控制。具体看以下注释:
1 | // |
spdlog 支持 6 种日志级别:
级别 | 说明 | 适用场景 |
---|---|---|
trace | 最详细的调试信息 | 开发调试 |
debug | 调试信息 | 开发环境 |
info | 一般信息 | 运行状态 |
warn | 警告 | 潜在问题 |
error | 错误(但程序可继续运行) | 异常情况 |
critical | 严重错误(可能崩溃) | 致命问题 |
其中的 debug 级别,稣总觉得不应该存在,根据情况归到 trace 或 info 即可。
spdlog::set_level 设置的是运行时的显示级别,比如说:
1 | spdlog::set_level(spdlog::level::debug); // 只显示 >= debug 的日志 |
但通常我们会使用宏来打印日志,并通过设定 SPDLOG_ACTIVE_LEVEL 来去掉低级别日志,以提高运行效率,或防止被“轻松逆向”。一般来说,Debug 版本可以设定 #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
,而 Release 版本可以 #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
或 #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_WARN
。
既然我们使用宏来打印日志,那么我们就能注意到:宏也有两类,一类形如 SPDLOG_LOGGER_INFO
,需要一个 logger 参数,另一类形如 SPDLOG_INFO
,不需要传入 logger 参数。从以下代码可知,后者是默认 Logger 的打印宏。
1 |
如果我们不想使用默认 Logger,就得自己创建 Logger,并使用第一类形如 SPDLOG_LOGGER_INFO
的宏,但这个宏有点长,还是把自己创建的 Logger 设置为默认,再使用第二类形如 SPDLOG_INFO
的宏方便点。
1 | inline bool InitializeLogger() { |
至于输出格式,稣的测试程序一般如此设定:
1 | int main(int argc, char* argv[]) { |
注意:默认情况下,spdlog 是同步的!如果日志量很大,应该使用异步日志(减少主线程阻塞)。这个……问掐鸡(LLM)吧!