网站首页> 文章专栏> 让spdlog非宏日志函数输出文件名行号
使用spdlog的宏函数可以输文件名行号等信息:
SPDLOG_INFO("{}", "test");
// 将输出:
[2022-12-12 08:48:56.185] [info] [main.cpp:27] test
如果改成普通日志函数:
spdlog::info("{}", "test");
// 将输出:
[2022-12-12 08:48:56.185] [info] test
这样就不会输出文件名行号信息了,有没有办法让spdlog的日志函数输出这些信息呢?如果这样写:
spdlog::default_logger_raw()->log({__FILE__, __LINE__, SPDLOG_FUNCTION},
spdlog::level::info, "{}", "test");
// 将输出:
[2022-12-12 08:49:03.625] [info] [main.cpp:29] test
这样可以输出文件名行号,因为在log函数参数里已经指定了,但是这个函数用起来太麻烦,而spdlog又没有提供更简单的能输出文件名行号的日志函数。
该怎么解决这个问题呢?雅兰亭库的easylog已经解决了这个问题:
easylog::info("{}", "test");
// 将输出:
[2022-12-12 08:49:56.165] [info] test
雅兰亭库的日志库easylog对spdlog做了一个简单的封装,日志函数的使用和spdlog一样,因为是纯转发,但是日志输出是带文件名和行号的。在目前一些编译器还没有支持std::source_location的情况下,easylog封装了一个跨平台的source_location,几行代码而已,然后将这个source_location转换成spdlog的source_location,最后调用spdlog::default_logger_raw()->log
。
// https://github.com/alibaba/yalantinglibs/blob/main/include/logging/easylog.hpp
struct source_location {
constexpr source_location(const char *file_name = __builtin_FILE(),
const char *function_name = __builtin_FUNCTION(),
unsigned int line = __builtin_LINE()) noexcept
: file_name_(file_name), function_name_(function_name), line_(line) {}
constexpr const char *file_name() const noexcept { return file_name_; }
constexpr const char *function_name() const noexcept {
return function_name_;
}
constexpr unsigned int line() const noexcept { return line_; }
private:
const char *file_name_;
const char *function_name_;
const unsigned int line_;
};
inline constexpr auto get_log_source_location(
const source_location &location) {
return spdlog::source_loc{location.file_name(),
static_cast<std::int32_t>(location.line()),
location.function_name()};
}
spdlog::default_logger_raw()->log(get_log_source_location(location),
spdlog::level::info, fmt,
std::forward<Args>(args)...);
接下来需要解决另外一个问题,直接用spdlog::default_logger_raw()->log
太麻烦了,如何简化接口的使用?
如果封装成这样一个接口呢?
void info(fmt::format_string<Args...> fmt, Args &&...args,
source_location location = {}) {
log(spdlog::level::info, location, fmt, std::forward<Args>(args)...);
}
使用source_location默认构造函数自动生成文件名行号,这样就可以简化接口了。但是这个代码会编译报错,因为变参之后不允许有参数,location放到变参后面是不行的,如果放到最前面又没办法用默认参数,如果放到中间则不符合fmt语法了,至此问题似乎无解了......
但是现代c++没有不可能,让dedution guide来帮忙!
```c++
template <typename... Args>
struct info {
constexpr info(fmt::format_string<Args...> fmt, Args &&...args,
source_location location = {}) {
log(spdlog::level::info, location, fmt, std::forward<Args>(args)...);
}
};
template <typename... Args>
info(fmt::format_string<Args...> fmt, Args &&...args) -> info<Args...>;
info构造函数使用了dedution guide, 告诉编译器显式推导Args...,这样变参就完全推导出来了,后面再加其它参数就没问题了。
以上。
现在你可以愉快的使用雅兰亭库easylog和fmt语法愉快的输出日志了!
地址: www.purecpp.cn
转载请注明出处!
purecpp
一个很酷的modern c++开源社区
purecpp社区自2015年创办以来,以“Newer is Better”为理念,相信新技术可以改变世界,一直致力于现代C++研究、应用和技术创新,期望通过现代C++的技术创新来提高企业生产力和效率。
社区坚持只发表原创技术文章,已经累计发表了一千多篇原创C++技术文章;
组织了十几场的C++沙龙和C++大会,有力地促进了国内外C++开发者之间的技术交流;
开源了十几个现代C++项目,被近百家公司所使用,有力地推动了现代C++在企业中的应用。
期待更多的C++爱好者能参与到社区C++社区的建设中来,一起为现代C++开源项目添砖加瓦,一起完善C++基础设施和生态圈。
微信公众号:purecpp, 社区邮箱: purecpp@163.com