一、概述
spdlog 是一个开源、高性能、跨平台的C++日志库,基于C++11实现,设计目标是兼顾易用性与极致性能 。其核心特点包括:
- 高性能: 核心设计目标,异步模式下可达数百万条/秒的日志吞吐量。
- 极简 API: 提供类似
spdlog::info("Hello {}!", "world")
的直观接口。 - 高度可扩展: 支持文件(带轮转)、控制台、系统日志、TCP 等多种输出目标 (Sinks)。
- 异步模式: 内置高性能异步日志,避免 I/O 阻塞主线程。
- 多线程安全: 所有组件均设计为线程安全。
- 灵活的格式化: 基于出色的
fmt
库,支持丰富格式定制。 - 轻量级: 纯头文件模式可选,易于集成。
- 活跃社区: 持续维护更新,文档完善。
二、安装与配置:快速集成
安装方式
-
包管理器 (推荐):
# vcpkg vcpkg install spdlog # Conan conan install spdlog/1.x.x
-
源码集成:
- 克隆仓库:
git clone https://github.com/gabime/spdlog.git
- 复制
include/spdlog
到项目头文件目录 - (可选) 编译并链接
spdlog
库 (SPDLOG_COMPILED_LIB
)
- 克隆仓库:
CMake 集成 (推荐)
find_package(spdlog REQUIRED) # 使用包管理器时
# 或
add_subdirectory(external/spdlog) # 源码集成时
target_link_libraries(YourTarget PRIVATE spdlog::spdlog)
基本使用
#include <spdlog/spdlog.h>
int main() {
// 设置全局日志级别 (debug, info, warn, error, critical)
spdlog::set_level(spdlog::level::debug);
// 输出日志
spdlog::debug("This is a debug message");
spdlog::info("Welcome to spdlog! {}", "Easy formatting");
spdlog::error("Something went wrong: {}", 42);
}
三、核心功能
1. 日志级别管理
spdlog::set_level(spdlog::level::warn); // 全局级别
auto logger = spdlog::get("my_logger");
logger->set_level(spdlog::level::debug); // 特定记录器级别
if (logger->should_log(spdlog::level::info)) {
... } // 条件检查
2. 记录器 (Loggers)
- 创建记录器:
// 基础控制台记录器 auto console_logger = spdlog::stdout_color_mt("console"); // 基础文件记录器 (覆盖) auto file_logger = spdlog::basic_logger_mt("file_logger", "logs/basic.txt"); // 创建自定义 sink 组合的记录器 std::vector<spdlog::sink_ptr> sinks; sinks.push_back(std::make_shared<spdlog::sinks::stdout_color_sink_mt>()); sinks.push_back(std::make_shared<spdlog::sinks::daily_file_sink_mt>("daily.log", 23, 59)); auto combined_logger = std::make_shared<spdlog::logger>("combined", begin(sinks), end(sinks)); spdlog::register_logger(combined_logger);
3. Sinks (输出目标)
- 常用 Sinks:
stdout_color_sink_mt
/stderr_color_sink_mt
(带颜色控制台)basic_file_sink_mt
(基础文件)rotating_file_sink_mt
(按大小轮转文件)daily_file_sink_mt
(按时间轮转文件)syslog_sink
(Unix 系统日志)msvc_sink_mt
(Windows 调试输出)tcp_sink_mt
(网络输出)
- Sink 配置:
auto sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>( "rotating.log", 1024 * 1024 * 5, 3); // 最大 5MB,保留 3 个备份 sink->set_level(spdlog::level::warn); // 设置该 sink 的过滤级别
4. 格式化 (Formatting)
- 自定义格式:
// 全局格式 spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v"); // 记录器特定格式 console_logger->set_pattern("%^[%H:%M:%S] [%l]%$ %v"); // %^/%$ 控制颜色范围 // 格式说明符示例: // %Y: 年, %m: 月, %d: 日, %H: 时, %M: 分, %S: 秒, %e: 毫秒 // %n: 记录器名称, %l: 级别, %v: 实际消息, %t: 线程 ID
5. 异步日志 (关键性能特性)
// 创建异步记录器 (带队列大小和线程数)
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>(
"async_file", "logs/async_log.txt");
// 全局设置异步线程池参数 (通常在程序开始)
spdlog::init_thread_pool(8192, 1); // 队列大小 8192,1 个工作线程
auto async_logger = std::make_shared<spdlog::async_logger>(
"async_log", sink, spdlog::thread_pool(), spdlog::async_overflow_policy::block);
spdlog::register_logger(async_logger);
- 溢出策略:
block
(阻塞生产者) 或overrun_oldest
(丢弃最旧消息)。
6. 多线程支持
- 所有
_mt
后缀的 Sinks 和 Loggers 都是线程安全的。 - 无需额外同步即可在多线程环境中使用。
7. 刷新策略
logger->flush_on(spdlog::level::err); // 遇到错误级别立即刷新
spdlog::flush_every(std::chrono::seconds(5)); // 定期刷新所有记录器
logger->flush(); // 手动强制刷新
8. 错误处理
// 注册全局错误处理器
spdlog::set_error_handler([](const std::string &msg) {
std::cerr << "spdlog error: " << msg << std::endl;
});
// 特定 sink 的错误处理 (如文件打开失败)
auto sink = std::make_shared<my_file_sink>();
sink->set_error_handler([](const std::string &msg) {
... });
四、应用案例:实战演示
案例 1:基础控制台 + 文件日志
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
int main() {
try {
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::info);
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("app.log", true);
file_sink->set_level(spdlog::level::debug);
spdlog::logger logger("main", {
console_sink, file_sink});
logger.set_level(spdlog::level::debug);
logger.info("Application started");
logger.debug("Debug details: {}", 42);
logger.warn("Low disk space!");
} catch (const spdlog::spdlog_ex &ex) {
std::cerr << "Log init failed: " << ex.what() << std::endl;
}
return 0;
}
案例 2:带轮转的异步日志系统
#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
int main() {
spdlog::init_thread_pool(32768, 2); // 大队列,2个线程
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>(
"logs/rotating.log", 1024 * 1024 * 50, 5); // 50MB, 5 files
std::vector<spdlog::sink_ptr> sinks{
stdout_sink, rotating_sink};
auto async_logger = std::make_shared<spdlog::async_logger>(
"async_system", sinks.begin(), sinks.end(),
spdlog::thread_pool(), spdlog::async_overflow_policy::block);
async_logger->set_level(spdlog::level::info);
spdlog::register_logger(async_logger);
// 使用异步记录器
SPDLOG_LOGGER_INFO(async_logger, "Async logging is running!");
async_logger->warn("This is a warning via async logger");
spdlog::shutdown(); // 确保程序退出前刷新所有日志
return 0;
}
案例 3:单例全局日志管理器 (常用模式)
// LoggerManager.h
#pragma once
#include <spdlog/spdlog.h>
#include <memory>
class LoggerManager {
public:
static spdlog::logger& getLogger() {
static auto logger = []() -> std::shared_ptr<spdlog::logger> {
auto logger = spdlog::stdout_color_mt("system");
logger->set_level(spdlog::level::debug);
logger->set_pattern("[%Y-%m-%d %T.%e] [%^%l%$] [%n] %v");
return logger;
}();
return *logger;
}
};
// 便捷宏
#define LOG_TRACE(...) SPDLOG_LOGGER_TRACE(LoggerManager::getLogger(), __VA_ARGS__)
#define LOG_INFO(...) SPDLOG_LOGGER_INFO(LoggerManager::getLogger(), __VA_ARGS__)
#define LOG_ERROR(...) SPDLOG_LOGGER_ERROR(LoggerManager::getLogger(), __VA_ARGS__)
// Usage in app.cpp
#include "LoggerManager.h"
int main() {
LOG_INFO("Application initialized");
LOG_ERROR("Critical error code: {}", 500);
}
五、进阶
1. 性能优化建议
- 优先使用异步日志: 对性能敏感场景至关重要。
- 避免同步日志 + 低级别刷盘: 如频繁的
flush()
或flush_on(low_level)
。 - 合理设置队列大小: 防止异步模式因队列满而阻塞。
- 谨慎使用全局锁: 默认线程安全有代价,极高并发场景可评估自定义无锁 sink。
2. 高级特性
- 自定义 Sink: 继承
spdlog::sinks::base_sink
实现任何输出目标。 - 自定义格式化器: 使用
fmt
库的强大功能定制字段。 - 日志过滤: 通过
set_level()
或自定义sink::should_log()
实现复杂过滤。 - 单头文件模式: 定义
SPDLOG_HEADER_ONLY
且不链接库 (方便但可能增加编译时间)。 - 二进制格式: 使用
spdlog::to_hex()
输出二进制数据。
3. 常见陷阱
- 未调用
spdlog::shutdown()
: 程序退出时可能丢失缓冲区日志 (RAII 通常能处理)。 - Sink 生命周期管理: 确保记录器使用的 sink 在其整个生命周期内有效。
- 格式字符串错误:
fmt
语法错误会导致运行时异常。 - 过度日志: 即使异步日志,过量日志仍会消耗内存和 CPU。
4. 资源
- 官方仓库: https://github.com/gabime/spdlog
- 详细文档: https://spdlog.docsforge.com/
- fmt 库文档: https://fmt.dev/latest/index.html (格式化基础)
六、总结
spdlog 将 C++ 日志记录提升到了新的高度。它完美平衡了 性能、易用性和功能性。无论是小型工具还是大型分布式系统,spdlog 都能提供可靠、高效的日志解决方案。其清晰的 API 设计、强大的异步能力、灵活的配置选项以及活跃的社区支持,使其成为现代 C++ 项目中日志组件的不二之选。
提示: 实际项目中,建议结合具体需求配置日志级别、轮转策略和输出目标,避免过度记录影响性能。善用异步模式,让应用飞起来!