如何为spdlog的日志格式添加自定义变量标识?
问题:如何在spdlog日志中固定显示自定义整型变量?
我在代码中使用spdlog进行日志记录,存在一个用于模拟步骤的重要整型变量,希望该变量能始终显示在日志中,期望的日志格式为:
[log_level][the_special_variable][logger_name] messages
请问该如何配置日志格式?是否有可行的实现方式?
补充说明:我已查阅spdlog官方GitHub仓库的Readme.md,看到以下自定义标识示例代码:
// Log patterns can contain custom flags. // the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance. #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_ptr<custom_flag_formatter> clone() const override { return spdlog::details::make_unique<my_formatter_flag>(); } }; void custom_flags_example() { auto formatter = std::make_unique<spdlog::pattern_formatter>(); formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v"); spdlog::set_formatter(std::move(formatter)); }
但我无法理解其用法,看起来该示例仅能为自定义标识添加字符串内容,想确认是否可以通过该方式显示整型变量?
解决方案:通过自定义flag实现动态整型变量的日志输出
完全可以通过spdlog的自定义flag机制实现这个需求,核心思路是让自定义flag类持有目标整型变量的引用,在格式化日志时将该变量的当前值转换为字符串并追加到日志中。以下是具体实现步骤:
1. 实现自定义flag类
修改官方示例中的自定义flag类,让它持有目标整型变量的引用,并在format方法中输出该变量的当前值。如果是多线程环境,需要为变量添加线程安全保护(比如用std::atomic<int>或者互斥锁)。
#include "spdlog/pattern_formatter.h" #include <atomic> // 自定义flag类,持有步骤变量的引用 class StepFlagFormatter : public spdlog::custom_flag_formatter { public: // 构造时传入步骤变量的引用 explicit StepFlagFormatter(std::atomic<int>& step_var) : step_var_(step_var) {} void format(const spdlog::details::log_msg&, const std::tm&, spdlog::memory_buf_t& dest) override { // 获取当前步骤值并转换为字符串 int current_step = step_var_.load(); std::string step_str = std::to_string(current_step); // 将字符串追加到日志缓冲区 dest.append(step_str.data(), step_str.data() + step_str.size()); } // 必须实现clone方法,确保每个logger都能拿到独立的flag实例 std::unique_ptr<custom_flag_formatter> clone() const override { return spdlog::details::make_unique<StepFlagFormatter>(step_var_); } private: std::atomic<int>& step_var_; // 持有步骤变量的引用,用atomic保证线程安全 };
2. 配置spdlog的格式化器
创建自定义格式化器,添加我们的flag,并设置符合需求的日志格式:
#include "spdlog/spdlog.h" int main() { // 定义全局/全局可见的步骤变量,用atomic保证多线程下的安全访问 std::atomic<int> simulation_step = 0; // 创建自定义格式化器 auto formatter = std::make_unique<spdlog::pattern_formatter>(); // 添加自定义flag,这里用'%s'作为标识(可自定义其他未被占用的字符) formatter->add_flag<StepFlagFormatter>('s', simulation_step) // 设置日志格式:[日志级别][步骤变量][logger名称] 消息内容 ->set_pattern("[%^%l%$][%s][%n] %v"); // 为默认logger设置格式化器 spdlog::set_formatter(std::move(formatter)); // 测试日志输出 spdlog::info("初始化完成"); simulation_step++; spdlog::warn("进入步骤1"); simulation_step++; spdlog::error("步骤2出现错误"); return 0; }
3. 输出效果
运行上述代码后,日志输出会符合预期格式:
[info][0][default] 初始化完成 [warn][1][default] 进入步骤1 [error][2][default] 步骤2出现错误
关键注意事项
- 线程安全:如果你的程序是多线程的,必须确保步骤变量的访问是线程安全的,推荐使用
std::atomic<int>或者配合std::mutex。 - clone方法:必须正确实现
clone方法,因为spdlog会为每个logger复制格式化器实例,确保每个实例都能正确引用到目标变量。 - flag字符选择:要选择spdlog默认未使用的字符作为自定义flag标识,避免冲突(默认已用的包括d、D、t、T、n、l、L、p、P、v等)。
内容的提问来源于stack exchange,提问作者Aaron Wu
相关产品推荐
相关产品推荐

