还在为C++项目的日志记录而烦恼吗?还在忍受着性能低下、配置复杂的古老日志库吗?是时候拥抱变化了!为你深度剖析当今最受欢迎的C++日志库之一------spdlog。它以其极致的性能、无与伦比的灵活性和极简的API设计,征服了无数开发者。无论你是刚入门的新手,还是追求极致性能的专家,这篇文章都将为你揭开spdlog的神秘面纱,让你彻底爱上打日志的感觉!
Github:github.com/gabime/spdl...
在软件开发,特别是服务端和高性能计算领域,日志(Logging)的重要性不言而喻。它不仅仅是调试的救命稻草,更是线上问题追溯、系统状态监控、数据分析的基石。然而,很多C++开发者,包括一些老手,仍在"刀耕火种"的时代:
printf
/std::cout
走天下: 简单直接,但毫无疑问,这是最低效、最不灵活的方式。无法分级、无法异步、无法结构化,在高并发场景下,IO阻塞更是性能杀手。- 老旧/自研日志库: 要么API设计反人类,要么配置复杂如"天书",要么性能堪忧,成为了项目中的"拖油瓶"。
如果你对以上场景感同身受,那么请允许我隆重介绍今天的主角------spdlog
。
为什么是 spdlog?它到底强在哪里?
spdlog
是一个现代化、功能极其丰富且性能极高的C++日志库。它的作者 Gabi Melman 是一位对性能有极致追求的开发者。下面我给大家总结一下它最吸引人的几个"杀手锏"特性:
1. 快,快到离谱 (Insanely Fast)
性能是 spdlog
最核心的设计目标。它在各种场景下的测试数据都名列前茅,尤其是在异步日志(Async Logging)模式下,其吞吐量可以达到每秒数百万条日志。
它是如何做到的?
- 底层基于
{fmt}
库: 使用了现代C++中性能最好的格式化库{fmt}
,远超iostream
和sprintf
。 - 无锁/低锁设计: 在异步模式下,
spdlog
使用了高效的无锁队列,最大程度地减少了多线程下的锁竞争开销。这意味着,你的业务线程在记录日志时几乎不会被阻塞。
2. 极致简单易用 (Header-Only & Simple API)
spdlog
可以作为 头文件库(Header-Only) 直接使用,你只需要把它引入到你的项目里,#include
相应的头文件即可,没有任何编译和链接的负担。
它的API设计得极为简洁优雅。忘掉那些繁琐的配置吧,感受一下:
C++
#include "spdlog/spdlog.h"
int main() {
// 使用默认的全局logger记录日志
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
}
是不是有种 Python
print()
函数的既视感?简洁、直观,并且类型安全。

3. 功能强大,灵活可扩展 (Feature-Rich & Extensible)
spdlog
不仅仅是快,它的功能也全面到令人发指。
-
多种日志目标 (Sinks): 你可以轻松地将日志输出到各种地方:
- 彩色控制台 (
stdout_color_mt
) - 普通文件 (
basic_file_sink_mt
) - 滚动文件(按大小切分) (
rotating_file_sink_mt
) - 每日文件(按时间切分) (
daily_file_sink_mt
) - 系统日志 (
syslog_sink_mt
) - 甚至可以组合多个
sink
,让一条日志同时输出到控制台和文件。
- 彩色控制台 (
-
自定义格式化: 你可以像
log4j
一样,通过简单的模式字符串(Pattern)来定制你的日志格式。C++// 设置日志格式: [时间] [logger名] [日志级别] [线程号] 日志内容 spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%n] [%^%l%$] [thread %t] %v"); spdlog::info("This is a formatted log entry.");
-
多线程与线程安全:
spdlog
的核心设计就考虑了多线程环境,提供了多种线程安全的logger
(_mt
后缀)。 -
异步日志模式: 只需一行代码,即可将同步的
logger
切换为异步模式,将IO操作放到独立的后台线程,彻底消除对关键路径的性能影响。C++#include "spdlog/async.h" #include "spdlog/sinks/basic_file_sink.h" int main() { // 初始化一个拥有8192个槽位的线程池 spdlog::init_thread_pool(8192, 1); auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt"); async_file->info("This is an async log message!"); }
上手实战:三步将 spdlog 集成到你的项目
说了这么多,我们来点实际的。
第一步:获取 spdlog
最简单的方式是直接从 GitHub 克隆:
bash
git clone https://github.com/gabime/spdlog.git
然后将 spdlog/include
目录添加到你的编译器头文件搜索路径中。
可以使用 -I
指定编译器搜索的路径,例如:
bash
g++ -I ./spdlog/include/ main.cpp -o main
第二步:创建并使用你的第一个 Logger
C++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include <vector>
#include <memory>
#include <iostream>
void create_and_use_logger() {
try {
// 创建一个彩色控制台sink
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::warn); // 控制台只输出 warning 及以上级别
// 创建一个5MB大小,3个备份的滚动文件sink
auto rotating_file_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024 * 1024 * 5, 3);
rotating_file_sink->set_level(spdlog::level::trace); // 文件记录所有 trace 及以上级别
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back(console_sink);
sinks.push_back(rotating_file_sink);
// 创建一个支持多 sink 的 logger
auto my_logger = std::make_shared<spdlog::logger>("my_logger", begin(sinks), end(sinks));
my_logger->set_level(spdlog::level::debug); // logger的总开关
// 注册 logger,方便全局访问
spdlog::register_logger(my_logger);
// 使用 logger
my_logger->info("This message will only go to the file.");
my_logger->warn("This message will go to both console and file.");
my_logger->error("Error with some arguments: {}, {}", "arg1", 123.45);
} catch (const spdlog::spdlog_ex& ex) {
std::cout << "Log initialization failed: " << ex.what() << std::endl;
}
}
int main() {
create_and_use_logger();
return 0;
}
第三步:编译并运行
由于是头文件库,直接编译你的源文件即可:
bash
g++ -I ./spdlog/include/ main.cpp -o main
运行后,你会在控制台看到 warn
和 error
级别的日志,同时在项目目录下会生成 mylog.txt
文件,记录了所有级别的日志。

一些进阶技巧
- 全局注册表 (Registry): 使用
spdlog::get("logger_name")
可以在项目的任何地方获取已注册的logger
,轻松实现模块化日志。 - 编译期日志级别控制: 通过定义
SPDLOG_ACTIVE_LEVEL
宏,可以在编译时就将低级别的日志调用完全移除,实现零开销 (Zero Overhead)。例如,在Release版本中设置SPDLOG_ACTIVE_LEVEL
为SPDLOG_LEVEL_INFO
,所有的trace
和debug
日志调用在编译后就"消失"了。 - Backtrace 支持:
spdlog
可以在发生错误时,自动记录最近的N条日志,这对于调试崩溃问题简直是神器。
总结
spdlog
完美地诠释了现代C++库应有的样子:高性能、易于使用、功能全面且高度灵活。它通过简洁的API隐藏了底层的复杂性,让开发者可以专注于业务逻辑,同时又能享受到高质量日志系统带来的所有好处。
如果你还在使用 printf
或者那些陈旧的日志库,我强烈建议你花上十分钟时间尝试一下 spdlog
。相信我,一旦你开始使用它,你就再也回不去了。它不仅仅是一个工具,更是一种能提升你开发体验和代码质量的"哲学"。