澳门永利皇宫中国官网入口 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_;
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 形状 | 无编译优化,AG真人2026世界杯中国官网编译手艺长 | 无需构建,永利官网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/
OD体育(ODSports)官网入口├── 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 澳门永利皇宫中国官网入口