Conky 集成 spdlog 实战指南:快速 C++ 日志库的安装、特性与工程化落地

发布时间:2026/9/25 13:00:41
Conky 集成 spdlog 实战指南:快速 C++ 日志库的安装、特性与工程化落地 桌面应用系统监控【免费下载链接】conkyLight-weight system monitor for X, Wayland, and other things, too项目地址https://gitcode.com/gh_mirrors/co/conky点击查看免费下载spdlog 是一个以快著称的 C 日志库它被作为第三方组件vendored内嵌在 Conky 仓库的 3rdparty/spdlog 目录中当前版本 1.17.0见 version.h为 Conky 的运行时诊断、跟踪trace与错误上报提供日志基础设施。读完本文你将掌握 spdlog 的两种接入方式、全部核心用法文件/控制台/轮转/每日/异步等十余种场景并能理解 Conky 如何在 src/logging.cc 中基于 spdlog 的自定义 flag、多 Sink 与线程局部上下文机制构建出生产级的日志系统。一、spdlog 是什么Conky 内嵌的高性能日志基础设施spdlog 是一个快速、特性丰富的 C 日志库同时支持仅头文件header-only与编译库compiled两种形态。在 Conky 仓库中它位于 3rdparty/spdlog 目录由 3rdparty/CMakeLists.txt 通过add_subdirectory(spdlog)纳入整体构建并在顶层 CMakeLists.txt 以spdlog::spdlog目标链接进conky_libs例如 src/CMakeLists.txt 中的tcp-portmon也直接复用该库。从仓库实现看Conky 的日志模块src/logging.cc、src/logging.h完全建立在 spdlog 之上使用stderr_color_sink_mt作为默认输出、可选接入 systemd journal 日志、利用spdlog::pattern_formatter注册自定义 flag。可以说理解 spdlog 的用法就等于理解 Conky 日志子系统的底层机制。二、两种使用模式Header-only 与编译版spdlog 官方提供两种接入方式各有适用场景仅头文件版本将include/spdlog目录整体拷贝到你的构建树中使用支持 C11 的编译器直接编译即可。优点是接入零成本缺点是每个包含它的编译单元都会展开大量模板代码编译时间较长。编译版官方推荐编译速度快得多$ git clone https://github.com/gabime/spdlog.git $ cd spdlog mkdir build cd build $ cmake .. cmake --build .Conky 仓库中采用的是编译库形态。从 3rdparty/spdlog/CMakeLists.txt 可以看到项目默认以add_library(spdlog STATIC ...)构建静态库并导出SPDLOG_COMPILED_LIB宏同时提供spdlog::spdlog别名目标此外还定义了spdlog::spdlog_header_only接口目标CMakeLists.txt供需要 header-only 的消费者使用。需要说明的是spdlog 的 C 标准最低要求是 C11而在 Conky 的集成中编译标准继承自父项目。编译期日志级别通过SPDLOG_ACTIVE_LEVEL宏控制未显式定义时默认值为SPDLOG_LEVEL_INFO见 common.h。Conky 在 src/logging.h 中显式定义为#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE即 Conky 构建出的二进制完整保留 trace 级别日志便于运行时通过参数动态调节详见下文日志级别与过滤。三、平台支持与包管理器安装spdlog 官方声明的支持平台包括Linux、FreeBSD、OpenBSD、Solaris、AIXWindowsmsvc 2013、cygwinmacOSclang 3.5Android除源码构建外多数系统可直接通过包管理器安装# Debian sudo apt install libspdlog-dev # Homebrew (macOS) brew install spdlog # MacPorts sudo port install spdlog # FreeBSD pkg install spdlog # Fedora dnf install spdlog # Gentoo emerge dev-libs/spdlog # Arch Linux pacman -S spdlog # openSUSE sudo zypper in spdlog-devel # ALT Linux apt-get install libspdlog-devel # vcpkg vcpkg install spdlog # conan conan install --requiresspdlog/[*] # conda conda install -c conda-forge spdlog # build2在 manifest 中声明依赖 depends: spdlog ^1.8.2注意Conky 仓库内嵌了完整的 spdlog 源码3rdparty/spdlog/src 下的async.cpp、cfg.cpp、color_sinks.cpp、file_sinks.cpp、spdlog.cpp、stdout_sinks.cpp等编译单元因此无需额外安装系统包即可构建 Conky。四、核心特性总览根据 3rdparty/spdlog/README.md 的 Features 清单spdlog 的核心能力可归纳为速度快见文末基准数据仅头文件或编译库两种形态丰富的格式化能力基于优秀的 fmt 库可选异步模式自定义格式化自定义 pattern flag多线程/单线程 logger多种日志目标Sink轮转文件、每日文件、控制台支持彩色、syslog、Windows 事件日志、Windows 调试器OutputDebugString(..)、Qt 控件以及易于扩展的自定义目标日志过滤日志级别可在运行时与编译期双重控制支持从 argv 或环境变量加载日志级别Backtrace 支持将调试消息存入环形缓冲区按需延迟输出。对应仓库中的实现文件均位于 3rdparty/spdlog/include/spdlog/sinksrotating_file_sink.h、daily_file_sink.h、hourly_file_sink.h、stdout_color_sinks.h、syslog_sink.h、msvc_sink.h、qt_sinks.h、kafka_sink.h、mongo_sink.h、ringbuffer_sink.h等各司其职。五、快速上手基础用法最基础的用法是直接使用 spdlog 提供的全局便捷函数它们操作一个默认 logger默认是彩色 stdout 多线程 logger#include spdlog/spdlog.h int main() { 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); spdlog::info(Support for floats {:03.2f}, 1.23456); spdlog::info(Positional args are {1} {0}.., too, supported); spdlog::info({:30}, left aligned); spdlog::set_level(spdlog::level::debug); // 设置*全局*日志级别为 debug spdlog::debug(This message should be displayed..); // 修改日志模式 spdlog::set_pattern([%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v); // 编译期日志级别 // 注意这不会改变当前日志级别只会取决于 SPDLOG_ACTIVE_LEVEL // 在发布代码中移除对应的调用。 SPDLOG_TRACE(Some trace message with param {}, 42); SPDLOG_DEBUG(Some debug message); }代码要点{}为 fmt 占位符支持宽度/对齐/进制等格式化语法{:08d}数字补零、{0:d}/{0:x}/{0:o}/{0:b}十进制/十六进制/八进制/二进制、{:03.2f}浮点精度、{1} {0}位置参数、{:30}左对齐set_level影响所有已注册 logger 的全局级别SPDLOG_TRACE/SPDLOG_DEBUG是编译期宏当SPDLOG_ACTIVE_LEVEL高于对应级别时调用在编译阶段即被剥离零运行时开销。Conky 在 src/logging.h 中将这些宏封装为LOG_TRACE / LOG_DEBUG / LOG_INFO / LOG_WARNING / LOG_ERROR / LOG_CRITICAL并先#undef掉系统头文件可能引入的同名宏上层业务代码只需包含该头文件即可统一使用。此外还提供了LOG_*_WITH(attrs, ...)系列宏src/logging.h在 trace 级别下为单条日志附加键值属性如LOG_INFO_WITH((deveth0), net up)。六、常用 Logger 工厂从控制台到文件spdlog 以logger 工厂的方式创建并注册具名 logger名称可用于后续从全局注册表中取回。6.1 stdout/stderr 彩色 logger#include spdlog/spdlog.h #include spdlog/sinks/stdout_color_sinks.h void stdout_example() { // 创建一个彩色多线程 logger auto console spdlog::stdout_color_mt(console); auto err_logger spdlog::stderr_color_mt(stderr); spdlog::get(console)-info(loggers can be retrieved from a global registry using the spdlog::get(logger_name)); }_mt后缀表示多线程安全内部加锁_st后缀表示单线程性能更高但只能单线程使用。spdlog::get(name)从全局注册表实现见 registry.h取回已注册的 logger。6.2 基础文件 logger#include spdlog/sinks/basic_file_sink.h void basic_logfile_example() { try { auto logger spdlog::basic_logger_mt(basic_logger, logs/basic-log.txt); } catch (const spdlog::spdlog_ex ex) { std::cout Log init failed: ex.what() std::endl; } }文件打开失败如目录不存在、无权限会抛出spdlog::spdlog_ex异常因此工厂调用建议包在 try/catch 中。6.3 轮转文件Rotating#include spdlog/sinks/rotating_file_sink.h void rotating_example() { // 创建一个最大 5 MB、保留 3 个轮转文件的文件 logger auto max_size 1048576 * 5; auto max_files 3; auto logger spdlog::rotating_logger_mt(some_logger_name, logs/rotating.txt, max_size, max_files); }当当前文件超过max_size字节时自动滚动到下一个编号文件超出max_files时淘汰最旧文件。实现细节可查阅 rotating_file_sink.h。6.4 每日文件Daily#include spdlog/sinks/daily_file_sink.h void daily_example() { // 创建一个每日 logger —— 每天凌晨 2:30 生成一个新文件 auto logger spdlog::daily_logger_mt(daily_logger, logs/daily.txt, 2, 30); }参数含义2为小时、30为分钟即每天 02:30 切换文件。更细粒度还有hourly_file_sink.h提供小时级轮转。6.5 文件打开/关闭事件回调// 可以在日志文件被打开/关闭前后获得回调 // 适用于做清理工作或在文件头/文件尾写入额外内容。 void file_events_example() { // 把 spdlog::file_event_handlers 传给文件 sink 即可 spdlog::file_event_handlers handlers; handlers.before_open [](spdlog::filename_t filename) { spdlog::info(Before opening {}, filename); }; handlers.after_open [](spdlog::filename_t filename, std::FILE *fstream) { fputs(After opening\n, fstream); }; handlers.before_close [](spdlog::filename_t filename, std::FILE *fstream) { fputs(Before closing\n, fstream); }; handlers.after_close [](spdlog::filename_t filename) { spdlog::info(After closing {}, filename); }; auto my_logger spdlog::basic_logger_st(some_logger, logs/events-sample.txt, true, handlers); }file_event_handlers是文件类 sink 的可选构造参数四个回调分别覆盖 open/close 的前后时机。6.6 替换默认 loggervoid replace_default_logger_example() { auto new_logger spdlog::basic_logger_mt(new_default_logger, logs/new-default-log.txt, true); spdlog::set_default_logger(new_logger); spdlog::info(new logger log message); }之后所有spdlog::info(...)等全局函数都会落到新 logger 上。Conky 在 src/logging.cc 正是用这种方式将默认 logger 替换为自定义的多 Sink loggerstd::make_sharedspdlog::logger(conky, sinks.begin(), sinks.end())后调用spdlog::set_default_logger(logger)。七、日志级别与过滤机制spdlog 的级别枚举定义在 common.h从低到高依次为枚举值说明trace最详细跟踪用debug调试信息info常规信息warn警告err错误critical致命off完全关闭级别过滤有三个层次运行时全局设置spdlog::set_level(spdlog::level::debug)作用于所有已注册 logger单 logger / 单 sink 设置logger-set_level(...)、sink-set_level(...)可实现控制台只显示 warn 以上、文件记录全部的差异化策略见下节多 Sink 示例编译期裁剪SPDLOG_ACTIVE_LEVEL决定SPDLOG_TRACE/SPDLOG_DEBUG等宏调用是否被编译进最终代码。此外spdlog::should_log(level)可查询当前默认 logger 是否会输出某级别spdlog::flush_on(level)可设置在达到某级别时立即 flush。7.1 从环境变量或 argv 加载级别#include spdlog/cfg/env.h int main (int argc, char *argv[]) { spdlog::cfg::load_env_levels(); // 或指定环境变量名 // MYAPP_LEVELinfo,myloggertrace ./example // spdlog::cfg::load_env_levels(MYAPP_LEVEL); // 或从命令行加载 // ./example SPDLOG_LEVELinfo,myloggertrace // #include spdlog/cfg/argv.h // 从 argv 加载级别 // spdlog::cfg::load_argv_levels(argc, argv); }然后即可在启动前设置环境变量默认变量名SPDLOG_LEVEL格式为全局级别,具名logger级别$ export SPDLOG_LEVELinfo,myloggertrace $ ./example对应实现为 cfg/env.h 与 cfg/argv.h。7.2 Conky 的运行时级别调节Conky 通过直接操作 stderr Sink 的级别实现运行时升降级src/logging.ccvoid conky::log::log_more() { auto lvl static_castint(stderr_sink-level()); if (lvl static_castint(spdlog::level::trace)) return; stderr_sink-set_level(static_castspdlog::level::level_enum(lvl - 1)); // 降一级显示更多 } void conky::log::log_less() { auto lvl static_castint(stderr_sink-level()); if (lvl static_castint(spdlog::level::off)) return; stderr_sink-set_level(static_castspdlog::level::level_enum(lvl 1)); // 升一级显示更少 } void conky::log::set_quiet() { stderr_sink-set_level(spdlog::level::off); }它把stderr_sink保存为全局静态对象src/logging.cc通过stderr_sink-level()与set_level()在 trace 与 off 之间逐级切换实现安静模式/更多日志等用户可感知的日志策略。八、格式化与日志模式Patternspdlog 默认 pattern 大致为[时间戳] [级别] 消息。通过spdlog::set_pattern(...)可自定义常用占位符包括%v消息正文、%nlogger 名、%t线程号、%l级别全称、%L级别首字母、%H:%M:%S时间、%z时区偏移、%e毫秒、%^/%$颜色区间。8.1 自定义 flag用户自定义模式指令除了内置 flagspdlog 允许通过继承custom_flag_formatter注册全新指令// 日志模式可以包含自定义 flag。 // 下面的例子新增 %* flag —— 绑定到一个 my_formatter_flag 实例。 #include spdlog/pattern_formatter.h class my_formatter_flag : public spdlog::custom_flag_formatter { public: void format(const spdlog::details::log_msg , const std::tm , spdlog::memory_buf_t dest) override { std::string some_txt custom-flag; dest.append(some_txt.data(), some_txt.data() some_txt.size()); } std::unique_ptrcustom_flag_formatter clone() const override { return spdlog::details::make_uniquemy_formatter_flag(); } }; void custom_flags_example() { auto formatter std::make_uniquespdlog::pattern_formatter(); formatter-add_flagmy_formatter_flag(*).set_pattern([%n] [%*] [%^%l%$] %v); spdlog::set_formatter(std::move(formatter)); }关键点format()负责输出、clone()负责为每个 sink 复制实例因为全局 formatter 会被各 logger/sink 克隆。8.2 Conky 的三个自定义 flag真实生产案例Conky 在 src/logging.cc 中基于上述机制定义了三个自定义 flagspan_formatter_flagflag 字符*从线程局部 span 栈读取当前上下文如[update_cb::update]仅 debug/trace 级别输出msg_attr_formatter_flagflag 字符输出单条消息的附加键值属性仅 trace 级别可见source_loc_formatter_flagflag 字符^输出[文件:行号:函数]并智能裁剪源码根路径、在 debug 级别以下隐藏函数名。最终组合出的 patternsrc/logging.ccformatter-set_pattern([%Y-%m-%d %H:%M:%S.%e] [%-5l]%^%* %v%);即[时间戳] [级别] [文件:行:函数] [span 上下文] 消息 {属性}—— 这正是 spdlog 自定义 flag 能力在大型项目中的典型落地也为读者编写自己的 flag 提供了完整范例。8.3 用户自定义类型的格式化通过特化fmt::formatter即可让自定义类型直接参与{}格式化template struct fmt::formattermy_type : fmt::formatterstd::string { auto format(my_type my, format_context ctx) const - decltype(ctx.out()) { return fmt::format_to(ctx.out(), [my_type i{}], my.i); } }; void user_defined_example() { spdlog::info(user defined type: {}, my_type(14)); }Conky 也采用同样的手法在 src/logging.h 中为std::filesystem::path特化了fmt::formatter使路径对象可直接LOG_INFO(dir {}, path)输出为字符串。九、Sinks多目标输出与多 Sink 组合Sink 是 spdlog 的输出目标抽象内置种类齐全轮转文件、每日文件、控制台彩色、syslog、Windows 事件日志、Windows 调试器、Qt 控件等且可通过继承sink接口轻易扩展自定义目标各实现见 3rdparty/spdlog/include/spdlog/sinks 目录。一个 logger 可以挂载多个 Sink并让每个 Sink 拥有独立的级别与格式// 创建一个带 2 个输出目标、且级别和格式各异的 logger。 // 控制台只显示 warn 及以上文件记录全部日志。 void multi_sink_example() { auto console_sink std::make_sharedspdlog::sinks::stdout_color_sink_mt(); console_sink-set_level(spdlog::level::warn); console_sink-set_pattern([multi_sink_example] [%^%l%$] %v); auto file_sink std::make_sharedspdlog::sinks::basic_file_sink_mt(logs/multisink.txt, true); file_sink-set_level(spdlog::level::trace); spdlog::logger logger(multi_sink, {console_sink, file_sink}); logger.set_level(spdlog::level::debug); logger.warn(this should appear in both console and file); logger.info(this message should not appear in the console, only in the file); }9.1 用户自定义回调 Sink// 创建带 lambda 回调的 logger每次有日志写入时回调被触发 void callback_example() { auto callback_sink std::make_sharedspdlog::sinks::callback_sink_mt([](const spdlog::details::log_msg msg) { // 例如发送邮件通知自己 }); callback_sink-set_level(spdlog::level::err); auto console_sink std::make_sharedspdlog::sinks::stdout_color_sink_mt(); spdlog::logger logger(custom_callback_logger, {console_sink, callback_sink}); logger.info(some info log); logger.error(critical issue); // 触发回调通知 }实现位于 callback_sink.h非常适合做告警、统计等旁路处理。9.2 Conky 的多 Sink 组合Conky 的 logger 初始化src/logging.cc展示了多 Sink 组合的真实用法std::vectorspdlog::sink_ptr sinks; stderr_sink std::make_sharedspdlog::sinks::stderr_color_sink_mt(); stderr_sink-set_level(spdlog::level::info); sinks.push_back(stderr_sink); #ifdef BUILD_JOURNAL auto journal_sink std::make_sharedspdlog::sinks::systemd_sink_mt(); journal_sink-set_level(spdlog::level::warn); sinks.push_back(journal_sink); #endif默认情况下 stderr 彩色 sink 从 info 级别起输出当启用BUILD_JOURNAL时额外挂载 systemd journal sinksystemd_sink.h仓库中对应实现即 Conky 对该组件的选用且只接收 warn 及以上的重要消息——避免刷屏 journal又保证关键错误不丢失。十、异步日志高性能路径异步模式下日志消息先进入无锁 MPSC 队列实现见 mpmc_blocking_q.h由独立后台线程批量写入目标主线程几乎零阻塞适合高频日志场景。10.1 基础异步 logger#include spdlog/async.h #include spdlog/sinks/basic_file_sink.h void async_example() { // 默认线程池参数可在创建异步 logger 之前修改 // spdlog::init_thread_pool(8192, 1); // 8192 槽位队列 1 个后台线程 auto async_file spdlog::basic_logger_mtspdlog::async_factory(async_file_logger, logs/async_log.txt); // 或者 // auto async_file spdlog::create_asyncspdlog::sinks::basic_file_sink_mt(async_file_logger, logs/async_log.txt); }spdlog::init_thread_pool(queue_size, threads)需在创建任何异步 logger之前调用以覆盖默认线程池。10.2 多 Sink 异步 logger#include spdlog/async.h #include spdlog/sinks/stdout_color_sinks.h #include spdlog/sinks/rotating_file_sink.h void multi_sink_example2() { spdlog::init_thread_pool(8192, 1); auto stdout_sink std::make_sharedspdlog::sinks::stdout_color_sink_mt (); auto rotating_sink std::make_sharedspdlog::sinks::rotating_file_sink_mt(mylog.txt, 1024*1024*10, 3); std::vectorspdlog::sink_ptr sinks {stdout_sink, rotating_sink}; auto logger std::make_sharedspdlog::async_logger(loggername, sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); spdlog::register_logger(logger); }10.3 队列溢出策略队列满时的行为由async_overflow_policy决定block调用线程阻塞等待队列腾出空间保证消息不丢失吞吐受限overrun丢弃最旧消息直接写入最新消息调用线程永不阻塞吞吐最高。README 中的异步基准数据同时给出了两种策略的对比overrun吞吐约为block的 45 倍。需要说明的是异步模式依赖线程局部存储的 MDC 功能不可用见下文 MDC 一节。十一、Backtrace 环形缓冲与定时刷新11.1 Backtrace延迟输出调试信息调试消息可以先存入环形缓冲区而非立即输出待出错时再按需倾倒出来// 调试消息可以存放在环形缓冲区中而不是立即输出。 // 当需要时例如发生错误时再 dump 到日志里。 // 需要时调用 dump_backtrace() 将其输出。 spdlog::enable_backtrace(32); // 在缓冲区中保存最近的 32 条消息 // 或 my_logger-enable_backtrace(32).. for(int i 0; i 100; i) { spdlog::debug(Backtrace message {}, i); // 尚未输出 } // 例如发生某些错误后 spdlog::dump_backtrace(); // 现在输出展示最近 32 条消息 // 或 my_logger-dump_backtrace(32)..对应实现为 backtracer.h 与 backtracer-inl.h顶部 API 在 spdlog.h 中声明enable_backtrace/disable_backtrace/dump_backtrace。11.2 周期性自动刷新// 每 3 秒刷新所有*已注册* logger // 警告仅当所有 logger 都是线程安全_mt logger时才可使用 spdlog::flush_every(std::chrono::seconds(3));flush_every会启动一个定时刷新线程确保日志及时落盘。底层实现见 periodic_worker.h 与 periodic_worker-inl.h。十二、实用工具Stopwatch、二进制 hex 与 MDC12.1 秒表Stopwatch// spdlog 秒表支持 #include spdlog/stopwatch.h void stopwatch_example() { spdlog::stopwatch sw; spdlog::debug(Elapsed {}, sw); spdlog::debug(Elapsed {:.3}, sw); // 保留 3 位小数 }stopwatchstopwatch.h可当作可格式化对象直接放入{}适合记录代码段耗时。12.2 二进制数据的十六进制输出// 各种 std::containerchar 容器类型都可以使用也支持 range。 // 格式 flag // {:X} - 大写输出 // {:s} - 字节之间不加空格 // {:p} - 每行开头不输出位置信息 // {:n} - 不按行切分输出 // {:a} - 未设置 :n 时附带 ASCII 列 #include spdlog/fmt/bin_to_hex.h void binary_example() { auto console spdlog::get(console); std::arraychar, 80 buf; console-info(Binary example: {}, spdlog::to_hex(buf)); console-info(Another binary example:{:n}, spdlog::to_hex(std::begin(buf), std::begin(buf) 10)); // 更多示例 // logger-info(uppercase: {:X}, spdlog::to_hex(buf)); // logger-info(uppercase, no delimiters: {:Xs}, spdlog::to_hex(buf)); // logger-info(uppercase, no delimiters, no position info: {:Xsp}, spdlog::to_hex(buf)); }通过 bin_to_hex.h 的spdlog::to_hex()配合格式 flag可清晰展示网络包、密钥等二进制内容。12.3 Mapped Diagnostic ContextMDC// MDC 是一个在线程局部存储中保存键值对字符串值的映射。 // 每个线程维护自己的 MDClogger 用它把诊断信息附加到日志输出。 // 注意由于依赖线程局部存储异步模式下不支持。 #include spdlog/mdc.h void mdc_example() { spdlog::mdc::put(key1, value1); spdlog::mdc::put(key2, value2); // 如果不使用默认格式用 % 输出 mdc 数据 // spdlog::set_pattern([%H:%M:%S %z] [%^%L%$] [%] %v); }实现见 mdc.h。值得注意的是Conky 复用了%这一 flag 字符来表达自己的单条消息附加属性见上文 8.2二者机制类似都是线程局部数据注入输出但用途不同spdlog 官方%输出 MDC 键值对Conky 的%输出随日志临时挂载的属性。十三、错误处理与系统集成13.1 自定义错误处理器void err_handler_example() { // 可全局设置也可按 logger 设置logger-set_error_handler(..) spdlog::set_error_handler([](const std::string msg) { spdlog::get(console)-error(*** LOGGER ERROR ***: {}, msg); }); spdlog::get(console)-info(some invalid message to trigger an error {}{}{}{}, 3); }当格式化参数数量不匹配等内部错误发生时会调用全局错误处理器默认打印到 stderr。13.2 syslog#include spdlog/sinks/syslog_sink.h void syslog_example() { std::string ident spdlog-example; auto syslog_logger spdlog::syslog_logger_mt(syslog, ident, LOG_PID); syslog_logger-warn(This is warning that will end up in syslog.); }使用 syslog_sink.h 可将日志送入系统 syslog 守护进程。13.3 Android#include spdlog/sinks/android_sink.h void android_example() { std::string tag spdlog-android; auto android_logger spdlog::android_logger_mt(android, tag); android_logger-critical(Use \adb shell logcat\ to view this message.); }Android 平台上日志会写入 logcat配合 android_sink.h 使用。13.4 日志到 Qt 控件带彩色#include spdlog/spdlog.h #include spdlog/sinks/qt_sinks.h MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { setMinimumSize(640, 480); auto log_widget new QTextEdit(this); setCentralWidget(log_widget); int max_lines 500; // 文本控件最多保留 500 行超出删除旧行 auto logger spdlog::qt_color_logger_mt(qt_logger, log_widget, max_lines); logger-info(Some info message); }qt_color_logger_mtqt_sinks.h可将日志实时渲染到QTextEdit等 Qt 控件适合图形界面程序内嵌日志视图。十四、注册多个 Logger 并批量设置级别// 创建 logger为所有已注册 logger 设置级别。 void set_level_example() { auto logger1 spdlog::basic_logger_mt(logger1, logs/logger1.txt); auto logger2 spdlog::basic_logger_mt(logger2, logs/logger2.txt); spdlog::set_default_logger(logger2); spdlog::default_logger()-set_level(spdlog::level::trace); // 将默认 loggerlogger2级别设为 trace spdlog::trace(trace message to the logger2 (specified as default)); spdlog::set_level(spdlog::level::off) // 原文如此将所有已注册 logger 级别设为 off禁用 logger1.warn(warn message will not appear because the level set to off); logger2.warn(warn message will not appear because the level set to off); spdlog::warn(warn message will not appear because the level set to off); }该示例演示了set_default_logger改变全局函数的落点、set_level批量作用于全部注册 logger、off级别整体静音。此外spdlog.h 还提供register_logger、register_or_replace、apply_all、drop(name)、drop_all()、shutdown()等注册表管理 API以及set_automatic_registration(false)关闭自动注册。十五、性能基准README 记录README 记录了在Ubuntu 64 位、Intel i7-4770 CPU 3.40GHz环境下的基准结果数据来自官方基准程序仅作量级参考实际性能随平台、编译器与配置不同而变化同步模式1,000,000 次迭代单线程[info] basic_st Elapsed: 0.17 secs 5,777,626/sec [info] rotating_st Elapsed: 0.18 secs 5,475,894/sec [info] daily_st Elapsed: 0.20 secs 5,062,659/sec [info] empty_logger Elapsed: 0.07 secs 14,127,300/sec同步模式400 字节 C 字符串单线程[info] basic_st Elapsed: 0.41 secs 2,412,483/sec [info] rotating_st Elapsed: 0.72 secs 1,389,196/sec [info] daily_st Elapsed: 0.42 secs 2,393,298/sec [info] null_st Elapsed: 0.04 secs 27,446,957/sec同步模式10 线程竞争同一 logger1,000,000 次迭代[info] basic_mt Elapsed: 0.60 secs 1,659,613/sec [info] rotating_mt Elapsed: 0.62 secs 1,612,493/sec [info] daily_mt Elapsed: 0.61 secs 1,638,305/sec [info] null_mt Elapsed: 0.16 secs 6,272,758/sec异步模式1,000,000 条消息、10 线程、队列 8,192 槽位、队列内存 8,192 × 272 2,176 KB[info] Queue Overflow Policy: block [info] Elapsed: 1.70784 secs 585,535/sec [info] Elapsed: 1.69805 secs 588,910/sec [info] Elapsed: 1.7026 secs 587,337/sec [info] Queue Overflow Policy: overrun [info] Elapsed: 0.372816 secs 2,682,285/sec [info] Elapsed: 0.379758 secs 2,633,255/sec [info] Elapsed: 0.373532 secs 2,677,147/sec数据规律单线程空 logger 与 null sink 吞吐最高多线程竞争下_mt系列仍有百万级/秒吞吐异步overrun策略能显著提升吞吐。这些数据侧面支撑了 Conky 选择 spdlog 作为日志基础设施的性能考量——系统监控程序需要高频、低干扰地记录状态变化。十六、Conky 工程化落地总结一套可复制的日志体系回顾 Conky 的集成读者可以从中提炼出一套可直接复用的 spdlog 工程实践多 Sink 分流src/logging.ccstderr 彩色 sinkinfo 起 可选 systemd journal sinkwarn 起实现开发看细节、系统只收关键事件自定义 formatter flagsrc/logging.ccsource_loc_formatter_flag%^、span_formatter_flag%*、msg_attr_formatter_flag%在单条日志中注入源码位置、调用栈上下文与结构化属性线程局部追踪上下文src/logging.hspan_guardLOG_SCOPE(name)宏src/logging.h自动维护 span 栈capture_context/install_context支持跨线程传播上下文宏封装与错误分级src/logging.hCRIT_ERR记录并std::terminate()、USER_ERR/SYSTEM_ERR记录并抛conky::error、COMMAND_ARG_ERR记录并抛参数错误统一了全项目的错误上报协议构建集成3rdparty/CMakeLists.txt 引入子目录顶层 CMakeLists.txt 与 src/CMakeLists.txt 通过spdlog::spdlog目标链接二进制随 Conky 一起编译分发。至此读者既掌握了 spdlog 从安装、基础用法到异步、backtrace、自定义格式化的完整技能树也看到了一个真实监控程序如何把它组织成生产级日志系统的完整范式——这套方法论可以平滑迁移到任何需要高效日志基础设施的 C 项目。赞分享桌面应用系统监控【免费下载链接】conkyLight-weight system monitor for X, Wayland, and other things, too项目地址https://gitcode.com/gh_mirrors/co/conky点击查看免费下载相关推荐如何快速构建高性能C日志系统fmt与spdlog集成终极指南在现代C开发中日志系统是应用程序不可或缺的核心组件。fmt库作为现代化的格式化工具库与spdlog高性能日志库的完美集成能够帮助开发者快速构建高效、可标准库10个spdlog实战示例C高性能日志库完全指南10个spdlog实战示例C高性能日志库完全指南 spdlog是一个专为C设计的高性能日志库提供快速、灵活的日志记录解决方案。本文通过10个实用示例后端C20日志新范式spdlog集成std::format全指南C20日志新范式spdlog集成std::format全指南 你还在为C日志格式化代码冗长而烦恼还在纠结第三方库依赖问题本文将带你解锁C20标后端上一篇virtCCA_driver源码解析深入理解驱动程序的核心实现原理下一篇openeuler/security-baseline5大核心功能让你的系统安全防护能力飙升创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考