抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

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

发现问题

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

使用GolangPython等语言开发过程序的开发者,应该都听说过 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 。

使用方式
1
LOG(日志等级) <<  “日志内容”;
支持的 Qt 变量类型
* * * * * *
QString QByteArray QLatin QList QVector QQueue
QSet QPair QMap QMultiMap QHash QMultiHash
QLinkedList QStack QChar q[u]int[64]

使用教程

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

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

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

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //  *.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++.cceasylogging++.h拷贝到要应用的项目目录;

  2. 配置 *.pro 文件

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // *.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. 初始化并输出一条日志

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    // *.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

评论