Qt轻量级日志库QsLog的使用

C加加下的日志库有很多,如log4cpp、Easylogging++等,Qt下也有log4qt。

不过我们今天要说的是QsLog,它是一个基于Qt的轻量级开源日志库。

git地址:https://github.com/victronenergy/QsLog

轻到什么程度,请看如下:

在这里插入图片描述

代码文件就是这么点。花点时间阅读一下,秒改造成你自己的私有轮子。

虽然轻量级,但是对于基本的日志功能需求还是够用的。

特征

  • 六个日志级别(从跟踪到致命)
  • 运行时可配置的日志级别阈值。
  • 关闭日志记录时的最小开销。
  • 支持多个目标,附带文件和调试目标。
  • 线程安全
  • 支持现成的常见Qt类型的日志记录。
  • 小依赖:直接把它放到你的项目中。

接下来,我们研究下它都有哪些功能。

一、QsLog使用方式

1. 源码集成

在你的工程中,直接包含QsLog.pri文件,进行源码集成。

当然你也可以包含QsLog.pri后,编译为xx.dll,在应用工程中去调用xx.dll。

2. 动态库集成

编译QsLogSharedLibrary.pro,生成动态链接库QsLog2.dll,在你的工程中进行调用。

二、日志级别

支持六个日志级别,优先级从低到高依次为:Trace、Debug、Info、Warn、Error、Fatal、Off。如下:

enum Level
{
    TraceLevel = 0,
    DebugLevel,
    InfoLevel,
    WarnLevel,
    ErrorLevel,
    FatalLevel,
    OffLevel
};
  • Trace:跟踪,最低等级的,用于打开所有日志记录。
  • Debug:调试,打印一些细粒度调试运行信息。
  • Info:信息,打印粗粒度信息,突出强调程序的运行过程。打印一些感兴趣或者重要的信息,可用于环境中输出程序运行的一些重要信息,但不能滥用,避免打印过多日志。
  • Warn:警告,表明会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示。
  • Error:错误,指出虽然发生错误,但仍然不影响系统的继续运行。打印错误和异常信息。
  • Fatal:致命的,发生严重错误,将导致应用程序的退出。这个级别比较高,程序直接停止运行了。
  • Off:最高等级的,用于关闭所有日志记录。

可以通过setLoggingLevel()设置记录日志的级别。

void setLoggingLevel(Level newLevel)

注意:

一般我们可以将日志级别保存到配置文件,以便程序发布后,可通过修改配置来改变记录日志级别。

三、日志输出目的地

QsLog的使用很简单,在我们自己的工程中直接include它的QsLog.pri文件,然后源文件中包含QsLog.h就可以使用了。

1. 输出到文件(支持文件分割)

// 测试文件为目的地
void test_output_file()
{
    // 初始化日志机制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加文件为目的地
    const QString sLogPath(QDir(QApplication::applicationDirPath()).filePath("log.txt"));
    DestinationPtr fileDestination(DestinationFactory::MakeFileDestination(
      sLogPath, EnableLogRotation, MaxSizeBytes(512*1024), MaxOldLogCount(5)));
    logger.addDestination(fileDestination);

    // 打印日志
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}
  • 代码很简单,setLoggingLevel()设置记录的日志级别;
    目前为TraceLevel,则日志级别比TraceLevel高的都会输出到文件;
    若为ErrorLevel,则只有"5-error msg"和"6-fatal msg"这2条会输出。

  • 然后设置文件名和输出目的地。其中目的地是由DestinationFactory::MakeFileDestination()函数进行构造的,其原型为:

static DestinationPtr MakeFileDestination(
    const QString& filePath,
    LogRotationOption rotation = DisableLogRotation,
    const MaxSizeBytes &sizeInBytesToRotateAfter = MaxSizeBytes(),
    const MaxOldLogCount &oldLogsToKeep = MaxOldLogCount());

函数参数含义:

filePath: 日志文件名

rotation: 取值DisableLogRotation和EnableLogRotation,

前者表示禁止日志文件分割,即日志始终往一个文件中写入。

后者表示启用日志文件分割,此时sizeInBytesToRotateAfter和oldLogsToKeep参数才有意义。

sizeInBytesToRotateAfter: 每个日志文件的字节数大小限制,即到达此大小后,自动新建文件,在新文件中进行写入。

oldLogsToKeep: 旧日志文件保留(备份)个数,超过此数量,自动删除最久远文件,备份文件最多支持10个。

若为2,则如下三个文件中内容,按照时间先后顺序排列为:log.txt.2->log.txt.1->log.txt,在log.txt中为最新日志,log.txt.1为次新,log.txt.2为最久远日志。

若此时log.txt超过sizeInBytesToRotateAfter限制,则会发生log.txt.2被删除,log.txt.1被改名log.txt.2,log.txt被改名log.txt.1,新建log.txt。

在这里插入图片描述

运行效果:

在这里插入图片描述

2. 输出到控制台stdout

// 测试stdout为目的地
void test_output_stdout()
{
    // 初始化日志机制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加stdout为目的地
    DestinationPtr debugDestination(DestinationFactory::MakeDebugOutputDestination());
    logger.addDestination(debugDestination);

    // 打印日志
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}

运行效果:

在这里插入图片描述

3. 输出到处理函数

void logFunction(const QString &message, QsLogging::Level level)
{
    qDebug() << "From log function: " << qPrintable(message) << " " << static_cast<int>(level);
}

// 测试函数为目的地
void test_output_function()
{
    // 初始化日志机制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加函数为目的地
    DestinationPtr functorDestination(DestinationFactory::MakeFunctorDestination(&logFunction));
    logger.addDestination(functorDestination);

    // 打印日志
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}

输出到函数,该函数需要定义为如下类型:

typedef void (*LogFunction)(const QString &message, Level level);

运行效果:

在这里插入图片描述

4. 输出到QTextEdit控件

除了上面的输出方式,还可以输出到一个QObject对象上,主要是通过信号槽机制,将打印日志发送到QObject的槽函数进行处理。

void MainWindow::writeLog(const QString &message, int level)
{
    ui->textEdit->append(message + " " + QString::number(level));
}
// 测试QObject为目的地
void test_output_qobject(MainWindow* window)
{
    // 初始化日志机制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加QObject为目的地
    DestinationPtr objectDestination(DestinationFactory::MakeFunctorDestination(window, SLOT(writeLog(QString,int))));
    logger.addDestination(objectDestination);

    // 打印日志
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}

输出到QObject时,需要定义其槽函数,为如下类型:

void xxxx(const QString &message, int level)

写本例子时,发现TRACE信息不能输出到QObject,是因为QsLogDestFunctor.cpp文件中,write函数有个bug,如下:

void QsLogging::FunctorDestination::write(const QString &message, QsLogging::Level level)
{
    if (mLogFunction)
        mLogFunction(message, level);

    if (level > QsLogging::TraceLevel)
        emit logMessageReady(message, static_cast<int>(level));
}

应将>改为>=,修改后即可解决,如下:

void QsLogging::FunctorDestination::write(const QString &message, QsLogging::Level level)
{
    if (mLogFunction)
        mLogFunction(message, level);

    if (level >= QsLogging::TraceLevel)
        emit logMessageReady(message, static_cast<int>(level));
}

修改后的QsLog库,下载链接:

https://download.csdn.net/download/u011832525/12520461

运行效果:

在这里插入图片描述

小结:

总结下,日志打印输出目的地可以有4种,分别是:

  • 输出到文件;
  • 输出到控制台stdout;
  • 输出到函数;
  • 输出到QObject。

并且可以添加任意多个目的地址,比如输出到文件的同时,还要输出到控制台进行显示,以方便查看调试打印信息。

四、打印源文件名称和行号

在QsLog.pri文件中

DEFINES += QS_LOG_LINE_NUMBERS

打开此宏定义,重新编译,即可打印带源文件名称和行号的日志。如下:

在这里插入图片描述

运行效果:

在这里插入图片描述

五、禁止日志记录

禁用日志记录,有时候关闭日志记录是有必要的。可以通过3种方式实现:

  • 全局地,在编译时,通过在QsLog.pri文件,打开DEFINES += QS_LOG_DISABLE宏定义。
  • 全局地,在运行时,通过将日志级别设置为关闭,即setLoggingLevel(QsLogging::OffLevel)。
  • 在编译时,通过在目标文件中包含QsLogDisableForThisFile.h,为每个文件创建一个。

六、线程安全

使用日志宏进行打印日志是线程安全的,日志宏如下:

QLOG_TRACE() << "1-trace msg";
QLOG_DEBUG() << "2-debug msg";
QLOG_INFO() << "3-info msg";
QLOG_WARN() << "4-warn msg";
QLOG_ERROR() << "5-error msg";
QLOG_FATAL()  << "6-fatal msg";

这在前面我们已经使用过了。

如setLoggingLevel()、addDestination()函数不是线程安全的。

七、日志的异步打印

所谓的异步打印,其实就是单独开一个线程来专门写日志。

在QsLog.pri文件中

DEFINES += QS_LOG_SEPARATE_THREAD

打开此宏定义,重新编译,日志内容就会在单独的线程中排队并写入。

八、总结

以下内容来自QsLog的git仓库介绍:

QsLog is an easy to use logger that is based on Qt’s QDebug class. QsLog is released as open source, under the MIT license.

###Contribution policy### Bug fixes are welcome, larger changes however are not encouraged at this point due to the lack of time on my side for reviewing and integrating them. Your best bet in this case would be to open a ticket for your change or forking the project and implementing your change there, with the possibility of having it integrated in the future. All contributions will be credited, license of the contributions should be MIT.

意思就是有问题,欢迎提bug,不过我没啥时间,修不修复就看我的心情了。如果你想玩,最好自己fork仓库,以后,万一未来哪天心情好了,想继续迭代版本,你敲的代码还是有可能merge进来的,到时候给你颁朵小红花,不过要遵守老麻的许可噢!

看完本文后,有需要的客官大老爷,请自行斟酌。

本文工程代码地址:

https://gitee.com/bailiyang/cdemo/tree/master/Qt/47QsLog


===================================================

===================================================

业余时间不定期更新一些想法、思考文章,欢迎关注,共同探讨,沉淀技术!

            

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章