上一讲正文我介绍了两个可以在 C++ 中进行单元测试的库。今天,类似的,我介绍两个实用的日志库,分别是 Easylogging++ [1] 和 spdlog [2]。
Easylogging++
事实上,我本来想只介绍 Easylogging++ 的。但在检查其 GitHub 页面时,我发现了一个问题:它在 2019 年基本没有更新,且目前上报的问题也没有人处理。这是个潜在问题,除非你觉得这个库好到愿意自己动手修问题(话说回来,这个库还是不错的,我在这个项目贡献了 8 个被合并的 pull request)。不管怎样,原先说了要介绍这个库,所以我也还是介绍一下。
概述
Easylogging++ 一共只有两个文件,一个是头文件,一个是普通 C++ 源文件。事实上,它的一个较早版本只有一个文件。正如 Catch2 里一旦定义了 CATCH_CONFIG_MAIN 编译速度会大大减慢一样,把什么东西都放一起最终证明对编译速度还是相当不利的,因此,有人提交了一个补丁,把代码拆成了两个文件。使用 Easylogging++ 也只需要这两个文件——除此之外,就只有对标准和系统头文件的依赖了。
要使用 Easylogging++,推荐直接把这两个文件放到你的项目里。Easylogging++ 有很多的配置项会影响编译结果,我们先大致查看一下常用的可配置项:
ELPP_UNICODE:启用 Unicode 支持,为在 Windows 上输出混合语言所必需
ELPP_THREAD_SAFE:启用多线程支持
ELPP_DISABLE_LOGS:全局禁用日志输出
ELPP_DEFAULT_LOG_FILE:定义缺省日志文件名称
ELPP_NO_DEFAULT_LOG_FILE:不使用缺省的日志输出文件
ELPP_UTC_DATETIME:在日志里使用协调世界时而非本地时间
ELPP_FEATURE_PERFORMANCE_TRACKING:开启性能跟踪功能
ELPP_FEATURE_CRASH_LOG:启用 GCC 专有的崩溃日志功能
ELPP_SYSLOG:允许使用系统日志(Unix 世界的 syslog)来记录日志
ELPP_STL_LOGGING:允许在日志里输出常用的标准容器对象(std::vector 等)
ELPP_QT_LOGGING:允许在日志里输出 Qt 的核心对象(QVector 等)
ELPP_BOOST_LOGGING:允许在日志里输出某些 Boost 的容器(boost::container::vector 等)
ELPP_WXWIDGETS_LOGGING:允许在日志里输出某些 wxWidgets 的模板对象(wxVector 等)
可以看到,Easylogging++ 的功能还是很丰富很全面的。
开始使用 Easylogging++
虽说 Easylogging++ 的功能非常多,但开始使用它毫不困难。我们从一个简单的例子开始看一下:
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
int main()
{
LOG(INFO) << "My first info log";
}
编译链接的时候要把 easylogging++.cc 放进去。比如,使用 GCC 的话,命令行会像:
g++ -std=c++17 test.cpp easylogging++.cc
运行生成的可执行程序,你就可以看到结果输出到终端和 myeasylog.log 文件里,包含了日期、时间、级别、日志名称和日志信息,形如:
2020-01-25 20:47:50,990 INFO [default] My first info log
如果你对上面用到的宏感到好奇的话, INITIALIZE_EASYLOGGINGPP 展开后(可以用编译器的 -E 参数查看宏展开后的结果)是定义了 Easylogging++ 使用到的全局对象,而 LOG(INFO) 则是 Info 级别的日志记录器,同时传递了文件名、行号、函数名等日志需要的信息。
使用 Unicode
如果你在 Windows 上,那有一个复杂性就是是否使用“Unicode”的问题( [第 11 讲] 中讨论了)。就我们日志输出而言,启用 Unicode 支持的好处是:
可以使用宽字符来输出
日志文件的格式是 UTF-8,而不是传统的字符集,只能支持一种文字
要启用 Unicode 支持,你需要定义宏 ELPP_UNICODE,并确保程序中有对 std::locale::global 或 setlocale 的调用(如 [第 11 讲] 中所述,只有进行了正确的区域设置,才能输出含非 ASCII 字符的宽字符串)。下面的程序给出了一个简单的示例:
#include <locale.h>
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
int main()
{
setlocale(LC_ALL, "");
LOG(INFO) << L"测试 test";
}
编译使用的命令行是:
cl /EHsc /DELPP_UNICODE tes