网站首页> 文章专栏> 让spdlog非宏日志函数输出文件名行号
让spdlog非宏日志函数输出文件名行号
编辑时间:2022-12-12 01:22:50 作者:purecpp 0条评论

使用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语法愉快的输出日志了!


    出自:purecpp.cn

    地址: www.purecpp.cn

    转载请注明出处!


来说两句吧
登录才能发表评论。
最新评论
Absolutely

purecpp

一个很酷的modern c++开源社区


[社区开源项目列表,点击前往]


purecpp社区自2015年创办以来,以“Newer is Better”为理念,相信新技术可以改变世界,一直致力于现代C++研究、应用和技术创新,期望通过现代C++的技术创新来提高企业生产力和效率。


社区坚持只发表原创技术文章,已经累计发表了一千多篇原创C++技术文章;


组织了十几场的C++沙龙和C++大会,有力地促进了国内外C++开发者之间的技术交流;


开源了十几个现代C++项目,被近百家公司所使用,有力地推动了现代C++在企业中的应用。


期待更多的C++爱好者能参与到社区C++社区的建设中来,一起为现代C++开源项目添砖加瓦,一起完善C++基础设施和生态圈。


微信公众号:purecpp, 社区邮箱: purecpp@163.com


友情链接