博亚(中国)体育app C++之高性能跨平台日记库spdlog

更多 C++ 著述见《 修远之路(C++集萃) 》专栏
spdlog 是一个基于 fmt 库的高性能、头文献优先的 C++ 日记框架,通过预分派环形队伍与异步线程池结束零拷贝日记记载。
超高速:异步步地可达百万条 / 秒隐隐,比 glog/log4cpp 快数倍。无锁遐想:异步用无锁队伍 + 线程池,干线程仅入队即复返,确凿不禁锢。零拷贝 / 内存池:减少内存分派,高并发更稳。同步 / 异步双步地:同步:平直写 I/O,肤浅平直。异步:后台线程处理 I/O,干线程无禁锢。中枢经由 #C++ #编程言语 #后端 #逐日精选著述中枢模块| 模块 | 中枢责任/作用 | 输入/输出 | | ---
| Logger | 日记级别过滤、音讯分发、罪过处理 调和进口,维持多 sink 组合 | 顺次化字符串 +
| 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 通过预分派与零拷贝遐想,在保证接口简约的前提下结束极致性能:
环形队伍:阵一火部分生动性(固定大小),同样零分派与缓存友好模板政策:编译期决定线程安全政策,零运行时支出异步解耦:业务线程仅肃肃入队,I/O 蔓延不影响中枢旅途关键空洞与机制环形队伍(Circular Queue)spdlog 的中枢地能优化来自预分派的环形队伍,其结束位于 circular_q.h :
template
class circular_q { size_t max_items_ = 0; typename std::vector::size_type head_ = 0; typename std::vector::size_type tail_ = 0; size_t overrun_counter_ = 0; std::vector v_; void push_back(T &&item) { if (max_items_ > 0) { v_[tail_] = std::move(item); tail_ = (tail_ +
if (tail_ == head_) { // 队伍满,笼罩最旧音讯 head_ = (head_ +
++overrun_counter_; } } } };
关键遐想点:
预分派:构造时刻派 max_items + 1 个元素,幸免运行时刻派笼罩政策:队伍满时自动笼罩最旧音讯,保证写入不禁锢零拷贝:使用 std::move 转变整个权,幸免深拷贝计数器: overrun_counter_ 记载丢失音讯数,用于监控MPMC 禁锢队伍多坐褥者-多奢靡者队诸位于 mpmc_blocking_q.h ,封装了环形队伍并提供线程安全:
template
class mpmc_blocking_queue { std::mutex queue_mutex_; std::condition_variable push_cv_; std::condition_variable pop_cv_; spdlog::details::circular_q q_; std::atomic discard_counter_{0}; void enqueue(T &&item) { { std::unique_lock lock(queue_mutex_); pop_cv_.wait(lock, [this] { return !this->q_.full; }); q_.push_back(std::move(item)); } push_cv_.notify_one; } bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration) { { std::unique_lock lock(queue_mutex_); if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty; })) { return false; } popped_item = std::move(q_.front); q_.pop_front; } pop_cv_.notify_one; return true; } };
并发领域政策:
双条目变量: push_cv_ 叫醒奢靡者, pop_cv_ 叫醒坐褥者细粒度锁:锁的抓有本事仅限于队伍操作,不包含 I/O超时机制: dequeue_for 维持超时复返,幸免死锁丢弃计数: discard_counter_ 记载 enqueue_if_have_room 失败次数Sink 空洞与线程安全Sink 接口界说于 sink.h :
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 sink_formatter) = 0;
void set_level(level::level_enum log_level);
level::level_enum level const;
bool should_log(level::level_enum msg_level) const;
protected:
level_t level_{level::trace};
};
线程安全通过模板政策结束,位于 base_sink.h :
template
class base_sink : public sink { public: void log(const details::log_msg &msg) final override { std::lock_guard lock(mutex_); sink_it_(msg); } void flush final override { std::lock_guard lock(mutex_); flush_; } protected: std::unique_ptr formatter_; Mutex mutex_; virtual void sink_it_(const details::log_msg &msg) = 0; virtual void flush_ = 0; };
遐想上风:
编译期多态:通过模板参数聘用 std::mutex 或 details::null_mutex零运行时支出:单线程场景使用 null_mutex ,无锁竞争CRTP 步地:基类领域经由,派生类结束具体逻辑顺次化器与本事戳缓存Pattern Formatter 位于 pattern_formatter.h :
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_;
澳门十大娱乐平台2026世界杯中国官方推荐std::vector> formatters_;
std::tm get_time_(const details::log_msg &msg) {
if (need_localtime_) {
auto time_now = log_clock::to_time_t(msg.time);
if (last_log_secs_ != time_now) {
cached_tm_ = localtime(time_now);
last_log_secs_ = time_now;
}
return cached_tm_;
}
return gmtime(log_clock::to_time_t(msg.time));
}
};
性能优化:
本事戳缓存:消灭秒内的日记分享 std::tm 结构编译期融会:步地字符串在构造时融会为 flag_formatter 链表内存复用: memory_buf_t 使用 fmt 的 memory_buffer ,幸免屡次分派中枢遐想弃取性能 vs 易用性| 遐想有狡计 | 性能收益 | 易用性代价 | 适用场景 | | ---
| Header-only 步地 | 无编译优化,编译本事长 | 无需构建,博亚体育app中国官网入口集成肤浅 | 快速原型、微型名堂 | | 编译步地 | 编译本事短,二进制体积小 | 需要 CMake 设立 | 坐褥环境、大型名堂 | | 异步步地 | 业务线程零禁锢 | 崩溃时可能丢失日记 | 高并发就业 | | 同步步地 | 日记规定严格保证 | I/O 禁锢业务线程 | 调试、审计场景 |
关键衡量:异步步地下,日记音讯在入队时已顺次化,但 log_msg_buffer 仍需拷贝 payload。这是为了确保业务线程开释原始字符串后,后台线程仍能安全访谒日记内容。
一致性 vs 可用性enum class async_overflow_policy {
block, // 禁锢直到有空间(强一致性)
overrun_oldest, // 笼罩最旧音讯(高可用性)
discard_new // 丢弃新音讯(低蔓延优先)
};
场景分析:
block:适用于审计日记,弗成容忍丢失,但可能禁锢业务线程overrun_oldest:适用于监控日记,容忍丢失旧数据,保确认时性discard_new:适用于调试日记,队伍满时丢弃新音讯,幸免禁锢空洞进度 vs 生动性Sink 空洞层级:
sink (接口)
└─ base_sink (模板基类,提供线程安全)
├─ basic_file_sink (单文献)
├─ rotating_file_sink (滚动文献)
├─ daily_file_sink (按日历分割)
└─ stdout_sink (领域台)
扩展机制:
自界说 Sink:收受 base_sink 并结束 sink_it_ 和 flush_自界说 Formatter:收受 custom_flag_formatter 并注册到 pattern_formatter自界说罪过处理:通过 set_error_handler 注册回调源码舆图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 # 天下类型界说,编译设立
│ │ ├── thread_pool.h # 异步线程池
│ │ ├── mpmc_blocking_q.h # 多坐褥者多奢靡者队伍
│ │ ├── circular_q.h # 环形队伍底层结束
│ │ ├── log_msg.h # 日记音讯结构
│ │ ├── log_msg_buffer.h # 带缓冲的音讯,用于异步
│ │ └── file_helper.h # 文献操作补助类
│ ├── 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 库编译结束
中枢文献融会:
logger.h :界说 logger 类,包含日记级别过滤、罪过处理、sink 惩处async_logger.h :重写 sink_it_ 和 flush_ ,将音讯推送到线程池mpmc_blocking_q.h :中枢并发数据结构,决定异步性能上限pattern_formatter.h :顺次化逻辑,本事戳缓存优化registry.h :全局景况惩处,logger 生命周期领域API 使用常用 API全局 API通过 spdlog:: 定名空间访谒
| 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: 罪过处理函数 | 成立罪过处理回调 |
| spdlog::create_async(name, args...) | 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、异步步地和资源计帐:
#include # include # include # include # include # include # include class GameLogger { public: static bool Initialize { try { // Initialize thread pool with 8192 queue size and 1 worker thread spdlog::init_thread_pool(8192, 1); // Create console sink (stdout with color) auto console_sink = std::make_shared; console_sink->set_level(spdlog::level::debug); console_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%^%l%$] [%t] %v"); // Create rotating file sink (5MB per file, max 3 files) auto file_sink = std::make_shared( "logs/game.log", 1024 *
file_sink->set_level(spdlog::level::info); file_sink->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] [%t] [%s:%#] %v"); // Combine sinks std::vector sinks{console_sink, file_sink}; // Create async logger auto logger = std::make_shared( "game_logger", sinks.begin, sinks.end, spdlog::thread_pool, spdlog::async_overflow_policy::block ); logger->set_level(spdlog::level::debug); logger->flush_on(spdlog::level::warn); // Set error handler logger->set_error_handler([](const std::string &msg) { std::cerr flush; }); // Release all loggers and stop threads spdlog::shutdown; } static void LogGameEvent(const std::string &event_name, int player_id, float value) { SPDLOG_INFO("GameEvent: {} [player={}, value={:.2f}]", event_name, player_id, value); } static void LogPerformanceMetric(const std::string &metric, double ms) { if (ms > 16.67) { // Below 60 FPS SPDLOG_WARN("Performance warning: {} took {:.2f}ms", metric, ms); } else { SPDLOG_DEBUG("Performance: {} = {:.2f}ms", metric, ms); } } };
int main { if (!GameLogger::Initialize) { return 1; } try { // Basic logging SPDLOG_INFO("Game started"); SPDLOG_DEBUG("Debug message (only visible in debug builds)"); // Formatted logging GameLogger::LogGameEvent("PlayerJump", 12345, 98.5f); GameLogger::LogPerformanceMetric("RenderFrame", 14.2); GameLogger::LogPerformanceMetric("PhysicsUpdate", 18.5); // Error handling SPDLOG_ERROR("Simulated error with code {}", 404); // Flush manually spdlog::default_logger->flush; } catch (const std::exception &ex) { SPDLOG_CRITICAL("Exception: {}", ex.what); GameLogger::Shutdown; return 1; } GameLogger::Shutdown; return 0; }
关键工程履行:
启动化规定:先启动化线程池,再创建 logger,临了注册罪过处理:拿获 spdlog::spdlog_ex 很是,成立罪过处理器资源计帐: shutdown 必须调用,不然异步线程不会罢手Flush 政策: flush_on(warn) 确保 warning 及以上司别立即写入Backtrace:存储最近 N 条日记,崩溃时 dump 用于调试场景提倡设立惩处// config/logger_config.h
struct LoggerConfig {
std::string log_file_path = "logs/app.log";
size_t max_file_size = 5 *
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); };
日记跟踪// Use MDC (Mapped Diagnostic Context) for request tracing
#include void HandleHttpRequest(const HttpRequest &req) { // Set trace ID for all logs in this scope spdlog::mdc::put("trace_id", req.trace_id); spdlog::mdc::put("user_id", std::to_string(req.user_id)); SPDLOG_INFO("Processing request"); // ... business logic ... spdlog::mdc::remove("trace_id"); spdlog::mdc::remove("user_id"); }
// Custom formatter with MDC support // Pattern: [%Y-%m-%d %H:%M:%S.%e] [%l] [trace:%X{trace_id}] %v
性能调优// Monitor async queue health
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 博亚(中国)体育app