说实话,spdlog 这库用起来是真的香,但“编译”这一步卡住过不少人。社区里到处都在说它是 header-only,拿过来 include 就能用,结果真到了项目里一编译,冒出来一堆链接错误、宏未定义、fmt 版本冲突,心态直接崩。我自己在 Windows 和 Linux 两个平台上都折腾过 spdlog 的编译,踩过的坑不算少,所以这篇就把整个编译处理的流程、CMake 开关、集成方式和报错排查链路完整串一遍。无论你只是想把 spdlog 编成静态库丢给项目用,还是想搞清楚那些 SPDLOG_COMPILED_LIB、SPDLOG_FMT_EXTERNAL 到底是什么意思,这篇文章都值得你花十分钟读完。
1. 先搞清楚一个关键问题:spdlog 到底要不要“编译”
1.1 header-only 模式是怎么回事
spdlog 默认确实支持 header-only,也就是说你把 include 目录加进工程,直接 #include <spdlog/spdlog.h>,就能正常创建 logger、写日志,不需要链接任何额外的库文件。它的实现思路很简单:所有模板代码、函数定义全部放在头文件里,编译器在每个翻译单元中直接展开实例化。对小型测试程序、工具脚本来说,这种模式极度方便,不需要处理任何构建依赖。
但方便是有代价的。每个包含了 spdlog 头文件的 .cpp 文件,都会独立编译一份完整的日志实现代码。假设你的项目有 50 个源文件,每个文件都 include 了 spdlog,那么同样的代码就被完整编译 50 遍。体现在构建体验上就是:全量编译时间明显拉长,增量编译时只要你动了和 spdlog 相关的公共头文件,基本等于全量重编。最终的二进制体积也会大一圈,因为每个编译单元里的日志代码副本都留在了目标文件里,链接器虽然能合并部分符号,但编译期的开销是实打实的。
1.2 compiled mode 的价值和适用场景
compiled mode(编译成库)就是解决上面那些问题的。spdlog 官方在源码里提供了 src/spdlog.cpp,里面显式实例化了所有核心代码。你用 CMake 构建时,他会把这段代码编译成独立的静态库或动态库,然后项目里只需要保留一份编译产物,头文件只负责声明接口。使用方需要额外定义一个 SPDLOG_COMPILED_LIB 宏,告诉库的头文件“我正在链接编译好的 spdlog,不要再按 header-only 的方式展开实现”。
这么做带来的直接好处有三点。第一,多源文件项目的编译时间大幅下降,我从 header-only 切到 compiled mode 之后,一个 40 个源文件的 C++ 项目全量编译时间从 6 分多钟降到了不到 2 分钟,提升是肉眼可见的。第二,动态库模式下,spdlog 的符号只存在于单独的 .dll 或 .so 文件中,多个子模块共享同一份日志实现,内部状态(比如全局注册表、sink 集合)保持一致,日志行为不会因为模块边界被拆散。第三,对嵌入式、游戏客户端这类对二进制体积敏感的场景,把日志库抽成独立模块,更容易控制最终产物体积和内存占用。
1.3 什么时候选哪种方案,我的建议
我个人的判断标准其实很简单。单文件工具、单元测试、快速原型,直接 header-only,没必要为编译一个库增加心智负担。正式的产品项目,只要源文件数量超过 20 个,或者存在多个动态库模块需要共享日志,一律编译成静态库或动态库。还有一种折中方案:项目本身用 header-only,但把 #include <spdlog/spdlog.h> 放进统一的预编译头文件(PCH)里,也能显著降低重复编译开销,这一招在 Windows + MSVC 环境下效果特别明显。
提示:spdlog 的官方 README 其实已经写清楚了,header-only 模式适合小项目和入门,严谨的生产环境建议使用 compiled mode。官方默认的 CMake 配置也倾向于编译成库,这一点从 CMakeLists.txt 里的默认选项就能看出来。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 编译前的准备工作:源码版本、工具链和依赖关系
2.1 源码获取方式与版本选择的坑
获取 spdlog 源码的方式主要有三种:GitHub Releases 页面下载压缩包、git clone 官方仓库(注意子模块)、或者通过 vcpkg / Conan 包管理器拉取。如果你只是要编译库文件,随便哪种都可以,但版本选择上有个细节容易被忽略:spdlog 的版本迭代中,CMake 最低版本要求、fmt 内部版本、C++ 标准要求都在变。比如 spdlog 1.12 之后要求 CMake 3.16+,而老版本的 1.9 只需要 CMake 3.10。如果你的构建服务器上 CMake 版本比较旧,直接拉最新版源码很可能第一步 configure 就报错。
所以我建议先确认两点:你本机 CMake 版本是多少,编译器支持到哪个 C++ 标准。然后去 GitHub Releases 挑一个与工具链匹配的版本,不要一上来就追最新。以我当时的使用经验,spdlog 1.11.0 是个非常稳定的版本,对 CMake 版本要求不高,fmt 内置版本也比较协调,如果项目没有特殊需求,从 1.11 或 1.12 入手最稳。
2.2 编译器与 C++ 标准的最低要求
spdlog 主体代码基于 C++11 编写,所以 GCC 4.8+、Clang 3.5+、MSVC 2015+ 理论上都能编译通过。但如果你开启了 SPDLOG_USE_STD_FORMAT 选项,也就是使用 C++20 标准库的 std::format 替代内置 fmt
