You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:18:21