C++日志可自动携带调用位置信息:一、用std::source_location默认参数;二、宏展开捕获location;三、可变模板+format;四、适配spdlog等库;五、__builtin_FILE/LINE兼容旧标准。

当您在C++项目中集成日志功能时,若希望每条日志自动携带调用位置信息(如文件名、行号、函数名),而无需手动传入宏参数,则可利用 std::source_location 特性配合日志库实现零侵入式堆栈上下文追踪。以下是具体实现方式:
一、使用默认参数绑定 std::source_location
该方法通过在日志函数签名中为 std::source_location::current() 设置默认参数,使编译器在每次调用时自动注入调用点信息,无需显式传参。
1、定义支持 source_location 的日志函数,声明最后一个参数为 const std::source_location& loc = std::source_location::current()。
2、在函数体内提取 loc.file_name()、loc.line()、loc.function_name() 并格式化为字符串。
立即学习“C++免费学习笔记(深入)”;
3、将提取的源位置字段与用户日志消息拼接后交由底层日志器输出。
二、封装宏展开为带 location 的调用
该方法借助预处理器宏,在日志宏展开时显式捕获当前 source_location,避免因内联或优化导致 location 信息指向日志封装函数而非真实调用点。
1、定义日志宏,例如 #define LOG_INFO(msg) logger::info(msg, std::source_location::current())。
2、确保宏中调用的函数接收 const std::source_location& 参数并直接使用其字段。
3、在宏内部不进行任何函数调用跳转,保证 std::source_location::current() 在宏展开处求值。
三、基于可变参数模板的日志接口
该方法利用模板推导和参数包转发,构建类型安全且支持 source_location 自动注入的日志接口,适用于需格式化输出的场景。
1、声明模板函数 template
2、使用 std::format 或 fmt::format 对 fmt 和 args 进行格式化,生成消息字符串。
3、将格式化结果与 loc.file_name()、loc.line()、loc.function_name() 组合成结构化日志条目。
四、适配现有日志库(如 spdlog)
该方法通过继承或包装 spdlog::logger 类,重载日志方法以注入 source_location,并保持原有 API 兼容性。
1、创建派生类 CustomLogger,继承自 spdlog::logger。
2、在派生类中重载 info()、warn() 等方法,增加 const std::source_location& 参数,默认值为 std::source_location::current()。
3、在重载方法中调用基类的 sink_it_ 方法,并将 source_location 字段作为额外属性写入日志消息的自定义字段(如 using spdlog::details::log_msg)。
五、使用 __builtin_FILE/__builtin_LINE 替代方案(兼容 C++17 及以下)
该方法在不支持 std::source_location 的旧标准下,通过 GCC/Clang 内建宏模拟等效行为,确保日志位置信息仍可获取。
1、定义宏 #define SOURCE_LOCATION {__builtin_FILE(), __builtin_LINE(), __func__},返回自定义结构体。
2、构造兼容结构体 struct source_loc { const char* file; int line; const char* func; };。
3、在日志函数中统一接收该结构体参数,并按相同逻辑提取字段参与日志组装。


















