C++之高性能跨平台日志库spdlog
作者:水悠悠予安
时间:2026-07-01
浏览:4
spdlog是C++高性能日志库,异步模式可达每秒百万条消息。采用无锁队列、环形队列与零拷贝设计,支持同步/异步双模式。核心模块包括Logger、Sink、Formatter、Registry、线程池及MPMC队列,通过预分配与模板策略实现极致性能。
本文目录
核心流程
核心模块
要理解 spdlog 的设计,先看它的几个核心模块各自扮演什么角色:| 模块 | 核心职责/作用 | 输入/输出 |
|---|---|---|
| Logger | 日志级别过滤、消息分发、错误处理,统一入口,支持多 sink 组合 | 格式化字符串 + 参数,输出 log_msg 对象 |
| Sink | 实际 I/O 操作、格式化输出,可扩展输出目标,单一职责 | 输入 log_msg 对象,输出字节流到目标 |
| Formatter | 消息格式化、时间戳处理、颜色标记,支持自定义格式,缓存优化 | 输入 log_msg 对象,输出格式化字符串 |
| Registry | 全局 logger 管理、配置分发、生命周期,集中管理,避免全局变量污染 | 输入 logger 注册请求,输出 logger 引用 |
| Thread Pool | 异步消息处理、后台线程调度,解耦业务线程与 I/O 线程 | 输入 async_msg 对象,调用 logger sink_it_ |
| MPMC Queue | 线程安全消息队列、阻塞/非阻塞策略,生产者-消费者解耦,零分配 | 输入 log_msg 对象,输出出队消息 |
| 能力 | 适用场景 | 不适用场景 |
|---|---|---|
| 同步日志 | 单线程应用、调试阶段、低频日志 | 高并发生产环境、性能敏感路径 |
| 异步日志 | 高并发服务、游戏引擎、实时系统 | 需要严格顺序保证、崩溃时日志不能丢失 |
| 环形缓冲 | 固定内存预算、可容忍消息丢失 | 需要持久化所有日志、审计场景 |
| 多 Sink 组合 | 同时输出到文件/控制台/网络 | 单一输出目标、极简场景 |
| 自定义格式 | 结构化日志、日志分析系统 | 标准格式即可满足需求 |
核心执行时序
同步日志执行流程

异步日志执行流程

原理与设计
spdlog 能在保持接口简洁的同时实现极致性能,靠的是预分配与零拷贝设计这套组合拳: 1. 环形队列:牺牲部分灵活性(固定大小),换来零分配与缓存友好 2. 模板策略:在编译期决定线程安全策略,运行时零开销 3. 异步解耦:业务线程只负责入队,I/O 延迟被隔离在后台,不干扰核心路径关键抽象与机制
环形队列(Circular Queue)
环形队列是 spdlog 性能优化的基石,实现位于 `circular_q.h`: ```cpp templateMPMC 阻塞队列
多生产者-多消费者队列位于 `mpmc_blocking_q.h`,在环形队列基础上封装了线程安全机制: ```cpp templateSink 抽象与线程安全
Sink 接口定义在 `sink.h`: ```cpp class sink { public: virtual ~sink() = default; virtual void log(const details::log_msg &msg) = 0; virtual void flush() = 0; virtual void set_pattern(const std::string &pattern) = 0; virtual void set_formatter(std::unique_ptr格式化器与时间戳缓存
Pattern Formatter 位于 `pattern_formatter.h`: ```cpp class pattern_formatter final : public formatter { private: std::string pattern_; std::string eol_; pattern_time_type pattern_time_type_; bool need_localtime_; std::tm cached_tm_; std::chrono::seconds last_log_secs_; std::vector核心设计取舍
性能 vs 易用性
| 设计决策 | 性能收益 | 易用性代价 | 适用场景 |
|---|---|---|---|
| Header-only 模式 | 无编译优化,编译时间长 | 无需构建,集成简单 | 快速原型、小型项目 |
| 编译模式 | 编译时间短,二进制体积小 | 需要 CMake 配置 | 生产环境、大型项目 |
| 异步模式 | 业务线程零阻塞 | 崩溃时可能丢失日志 | 高并发服务 |
| 同步模式 | 日志顺序严格保证 | I/O 阻塞业务线程 | 调试、审计场景 |
一致性 vs 可用性
```cpp enum class async_overflow_policy { block, // 阻塞直到有空间(强一致性) overrun_oldest, // 覆盖最旧消息(高可用性) discard_new // 丢弃新消息(低延迟优先) }; ``` 不同场景下该选哪种策略? - block:审计日志的首选,不能容忍任何丢失,代价是可能阻塞业务线程 - overrun_oldest:监控日志的好选择,能容忍旧数据丢失,保证实时性 - discard_new:调试日志的理想方案,队列满了直接丢弃新消息,避免阻塞抽象程度 vs 灵活性
Sink 的继承层级相当清晰: ``` sink (接口) └─ base_sink源码地图
``` spdlog-1.15.2/ ├── include/spdlog/ │ ├── spdlog.h # 全局 API 入口,registry 访问 │ ├── logger.h # 核心 logger 类,同步日志实现 │ ├── async_logger.h # 异步 logger,继承 logger │ ├── formatter.h # 格式化器接口 │ ├── pattern_formatter.h # 默认格式化器实现 │ ├── async.h # 异步工厂函数 │ ├── common.h # 公共类型定义,编译配置 │ │ │ ├── details/ │ │ ├── registry.h # 全局 logger 注册表 │ │ ├── thread_pool.h # 异步线程池 │ │ ├── mpmc_blocking_q.h # 多生产者多消费者队列 │ │ ├── circular_q.h # 环形队列底层实现 │ │ ├── log_msg.h # 日志消息结构 │ │ ├── log_msg_buffer.h # 带缓冲的消息,用于异步 │ │ └── file_helper.h # 文件操作辅助类 │ │ │ └── sinks/ │ ├── sink.h # Sink 接口定义 │ ├── base_sink.h # 线程安全模板基类 │ ├── basic_file_sink.h # 基础文件 Sink │ ├── rotating_file_sink.h # 滚动文件 Sink │ ├── daily_file_sink.h # 日期分割 Sink │ ├── stdout_sinks.h # 控制台 Sink │ └── dist_sink.h # 分发 Sink(多目标) │ └── src/ ├── spdlog.cpp # 编译模式实现 ├── async.cpp # 异步相关实现 └── bundled_fmtlib_format.cpp # fmt 库编译实现 ``` 几个核心文件需要重点关注: 1. logger.h:定义了 `logger` 类,涵盖日志级别过滤、错误处理、sink 管理 2. async_logger.h:重写了 `sink_it_()` 和 `flush_()`,负责把消息推送到线程池 3. mpmc_blocking_q.h:核心并发数据结构,直接决定异步性能的上限 4. pattern_formatter.h:格式化逻辑,时间戳缓存优化是性能关键 5. registry.h:全局状态管理,logger 的整个生命周期都靠它掌控API 使用
常用 API
全局 API
通过 `spdlog::` 命名空间可以直接访问:| API | 参数说明 | 功能说明 |
|---|---|---|
spdlog::info(fmt, args...) | fmt: 格式化字符串,args: 参数 | 使用默认 logger 输出 INFO 级别日志 |
spdlog::set_level(level) | level: level::trace 到 level::off | 设置全局日志级别 |
spdlog::set_pattern(pattern) | pattern: 格式模式字符串 | 设置全局格式模式 |
spdlog::get(name) | name: logger 名称 | 获取已注册的 logger,不存在返回 nullptr |
spdlog::drop(name) | name: logger 名称 | 从 registry 移除 logger |
spdlog::shutdown() | 无 | 停止所有线程,清理资源 |
spdlog::flush_on(level) | level: 触发 flush 的级别 | 设置自动 flush 级别 |
spdlog::flush_every(interval) | interval: 时间间隔 | 启动周期性 flush 线程 |
Logger API
| API | 参数说明 | 功能说明 |
|---|---|---|
logger->log(level, fmt, args...) | level: 日志级别 | 输出指定级别日志 |
logger->set_level(level) | level: 日志级别 | 设置该 logger 的日志级别 |
logger->flush() | 无 | 手动 flush 所有 sink |
logger->sinks() | 无 | 返回 sink 列表引用,可动态添加 sink |
logger->set_formatter(formatter) | formatter: 格式化器指针 | 设置自定义格式化器 |
logger->error_handler() | 无 | 获取当前错误处理器 |
logger->set_error_handler(handler) | handler: 错误处理函数 | 设置错误处理回调 |
Sink API
| API | 参数说明 | 功能说明 |
|---|---|---|
sink->set_level(level) | level: 日志级别 | 设置 sink 级别过滤 |
sink->set_pattern(pattern) | pattern: 格式模式 | 设置 sink 格式 |
sink->flush() | 无 | flush 该 sink |
异步 API
| API | 参数说明 | 功能说明 |
|---|---|---|
spdlog::create_async | Sink: sink 类型,name: logger 名称 | 创建异步 logger |
spdlog::init_thread_pool(q_size, n_threads) | q_size: 队列大小,n_threads: 线程数 | 初始化全局线程池 |
async_logger(logger_name, sink, tp, policy) | tp: 线程池,policy: 溢出策略 | 构造异步 logger |
样例 Demo
下面是一个完整的示例,涵盖了错误处理、多 sink、异步模式和资源清理: ```cpp #include场景建议
配置管理
```cpp // config/logger_config.h struct LoggerConfig { std::string log_file_path = "logs/app.log"; size_t max_file_size = 5 * 1024 * 1024; // 5MB int max_files = 3; spdlog::level::level_enum level = spdlog::level::info; bool async_mode = true; size_t queue_size = 8192; std::string pattern = "[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] %v"; static LoggerConfig LoadFromFile(const std::string &config_path); }; ```日志追踪
```cpp // 使用 MDC(Mapped Diagnostic Context)进行请求追踪 #include性能调优
```cpp // 监控异步队列健康状态 void MonitorLoggerQueue() { auto tp = spdlog::thread_pool(); if (tp) { size_t overrun = tp->overrun_counter(); size_t discard = tp->discard_counter(); size_t queue_size = tp->queue_size(); if (overrun > 0 || discard > 0) { SPDLOG_WARN("Logger queue issues: overrun={}, discard={}, size={}", overrun, discard, queue_size); } // 监控完后重置计数器 tp->reset_overrun_counter(); tp->reset_discard_counter(); } } // 编译期日志级别控制,release 版本屏蔽 debug 日志 #ifndef NDEBUG #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG #else #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO #endif // 使用宏代替函数调用,编译期自动过滤 SPDLOG_INFO("This call may be optimized out in release"); ```监控集成
```cpp // 自定义 sink 收集指标 class MetricsSink : public spdlog::sinks::base_sink
作者最新文章
动漫共和国在线观看地址及入口
2026-11-04 12:06
立刻MV如何查看立刻MV官方操作指引文档
2026-11-02 14:05
抖音创作平台官网登录入口
2026-11-02 10:38
Xshell连接SSH失败怎么办?5步排查网络、代理与认证问题
2026-10-30 14:30
樱花动漫官方正版官网入口在哪_樱花动漫网址2027最新分享
2026-10-28 19:21
上一篇:
Apache配置时如何实现防盗爬虫
热门文章
更多
精品专题
更多
Mac软件
更多
WINDOWS
更多

































