Qt程序中优雅的日志方案

Qt程序中优雅的日志方案

随笔 📅 2021-03-31 ⏱ 约 10 分钟
✦
AI 摘要AI Abstract
  • Easylogging++ 是 Qt C++ 中媲美 Loguru 的优雅日志方案,轻量、高效且高度可配置。
  • 日志核心要素包括时间、位置、级别、内容、唯一标识和格式化,需根据用途合理设计。
  • Easylogging++ 支持 Qt 数据类型(如 QString、QList 等),配置简单,适合多环境分级输出。
  • 通过 *.pro 文件定义宏启用 Qt 支持,结合 LOG(INFO) 语法即可快速集成到项目中。

一个完整的软件(系统)都要有一套优雅的日志方案。

发现问题

在使用Qt开发软件(系统)过程中,出于多种目的,开发者难免需要记录程序运行过程中的一些状态信息,也就是我们常说的日志。Qt 中的日志模块(qInstallMessageHandler)配置繁杂,对于日志设计新手来说很不友好,虽然官方给出了Demo,但其繁杂程度依旧不减。

使用Golang、Python等语言开发过程序的开发者,应该都听说过 Loguru,它是在流行语言领域优雅日志方案的佼佼者。在Qt C++ 开发中有没有Loguru或者和它媲美的日志方案呢?

问题调研

在寻找好的日志记录方案前,有必要先了解清楚两个问题,我们为什么要记录日志?日志记录的要素有哪些?

为什么要记录日志?

1.开发调试

目的是开发期调试程序使用,这种日志量比较大,且没有什么实质性的意义,只应该出现在开发期,而不应该在项目上线之后输出。

2.记录用户行为

这种类型的日志,记录用户的操作行为,用于大数据分析,比如监控、风控、推荐等等。这种日志,一般是给其他团队分析使用,而且可能是多个团队,因此一般会有一定的格式要求,开发者应该按照这个格式来记录,便于其他团队的使用。当然,要记录哪些行为、操作,一般也是约定好的,因此,开发者主要是执行的角色。

3.记录程序运行情况

记录程序的运行状况,特别是非预期的行为、异常情况,这种日志,主要是给开发、维护人员使用。什么时候记录,记录什么内容,完全取决于开发人员,开发者具有高度自主性。

4.记录系统、机器状况

比如网络请求、系统CPU、内存、IO使用情况等等,这种日志主要是给运维人员使用,生成各种更直观的展现形式,配合预警系统,在系统出问题时及时预警。

日志记录的要素有哪些?

每条日志都可以被当作一个事件(event),记录了该事件发生时各种信息,这些信息大概有以下几类。

1.时间

这个时间通常是指事件发生的时间,并不是日志被打印的时间。它通常用标准时间或时间戳的形式输出。

2.位置

这个位置一般有模块位置、文件位置、函数位置、代码所在行等等,根据敏感程度的不同,我们输出位置的级别也不同。

3.级别

级别也就是日志的重要程度,主要用于不同的环境(测试、生产)下,打印不同级别的日志;不同级别的日志产生不同级别的监控报警。

4.内容

内容也就是我们要展示给日志阅读者和使用者的信息,要简明扼要地描述这个模块、函数、文件等发生了什么事情。

5.唯一标识

在大型集群系统、分布式系统中,为了快速定位日志输出点,我们需要唯一标识作为日志的主体。

6.格式化

将上述的信息按照固定的格式打印出来,或输出到文件中,就是日志的格式化。这样做的目的不仅仅是方便阅读,还能方便后期编写日志解析程序。

7.其他

根据程序、业务等特点,还可以在日志中记录(包括但不限于):错误次数、处理进度、IP地址等等。

解决问题

明白了上述内容,我们可以在遵循 使用框架或模块、不能出错、避免敏感信息、合理归档分类等原则的情况下,选择一个合适的框架来达到我们的目的。

Easylogging++

如同 Loguru 一样,在 Qt C++ 中也可以有很优雅的日志方案,它就是集简单、轻量、高效、可配置等优点于一身的 Easylogging++。

虽然也有 C++ 版本的 Loguru ,但它的功能显然对 Qt 的支持不是很友好。于是我们选用了通用性较强的 Easylogging++ 来达到我们的目的。

入门知识

日志等级

常用的日志等级,由高到低,依次为 Error 、 Debug 、 Warning 、 Info 。

使用方式
LOG(日志等级) <<  “日志内容”;
支持的 Qt 变量类型
******
QStringQByteArrayQLatinQListQVectorQQueue
QSetQPairQMapQMultiMapQHashQMultiHash
QLinkedListQStackQCharq[u]int[64]

使用教程

1)下载或克隆 Easylogging++ 仓库源码

Github地址: https://github.com/amrayn/easyloggingpp

2) 原生 C++ 方式使用
  1. 将 src 文件夹下的 easylogging++.cc 和 easylogging++.h拷贝到要应用的项目目录;

  2. 初始化并输出一条日志

    //  *.cpp
     
    // 导入头文件并初始化
    #include "easylogging++.h"
    INITIALIZE_EASYLOGGINGPP
     
    // 输出日志
    int main(int argc, const char* argv[]) 
    {
        LOG(INFO) << "This is a log generated by Easylogging++.";
    }
     
  3. 注意事项

    • 所有要输出的变量类型必须为标准 C++ 格式
    • 初始化必须在 *.cpp文件中进行
3)Qt 调用方式
  1. 将 src 文件夹下的 easylogging++.cc 和 easylogging++.h拷贝到要应用的项目目录;

  2. 配置 *.pro 文件

    // *.pro
     
    // 写入配置
    DEFINES += ELPP_QT_LOGGING    \
              ELPP_FEATURE_ALL \
              ELPP_STL_LOGGING   \
              ELPP_STRICT_SIZE_CHECK ELPP_UNICODE \
              ELPP_MULTI_LOGGER_SUPPORT \
              ELPP_THREAD_SAFE
              
  3. 初始化并输出一条日志

    // *.cpp
     
    // 导入头文件并初始化
    #include "easylogging++.h"
    INITIALIZE_EASYLOGGINGPP
     
    #include "logdemo.h"
    #include "ui_logdemo.h"
     
     
    LogDemo::LogDemo(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::LogDemo)
    {
        ui->setupUi(this);
        InitLogger();
    }
     
    LogDemo::~LogDemo()
    {
        delete ui;
    }
     
    /** 日志初始化
     * @brief LogDemo::InitLogger
     */
    void LogDemo::InitLogger()
    {
        QString s = "This is a log generated by Easylogging++.";
        LOG(INFO) << s;
    }
  4. 注意事项

    • *.pro必须加入相应配置
    • 所有要输出的变量类型可以为 受支持的Qt格式或标准 C++ 格式
    • 初始化必须在 *.cpp文件中进行

更多拓展

  • 独立配置文件
  • 自定义 Logger
  • 多线程
  • 导出成动态链接库
  • …

参考链接

[1] 知乎专栏 - 记录日志有哪些好的技巧?

[2] 静觅 - Python 中更优雅的日志记录方案 loguru

[3] Github - Easyloggingpp

评论