返回列表
基于Qt开发的日志库QsLog的使用

基于Qt开发的日志库QsLog的使用

概述

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

这里介绍QsLog,它是一个基于Qt的轻量级开源日志库。

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

源代码结构如下图:

图片1.png

特征

六个日志级别(从跟踪到致命)

运行时可配置的日志级别阈值。

关闭日志记录时的最小开销。

支持多个目标,附带文件和调试目标。

线程安全

支持现成的常见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就可以使用了。日志打印输出目的地可以有4种,分别是:

1. 输出到文件;

2. 输出到控制台stdout;

3. 输出到函数;

4. 输出到QObject。

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

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。

图片3.png

运行效果

图片4.png

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();

}

运行效果:

图片5.png

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);

运行效果:

图片6.png

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));

}

运行效果:

图片7.png

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

QsLog.pri文件中

DEFINES += QS_LOG_LINE_NUMBERS

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

图片8.png

运行效果

图片9.png

五、禁止日志记录

禁用日志记录,有时候关闭日志记录是有必要的。可以通过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

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


网站编辑:小优智能科技有限公司 发布时间:Aug 30,2024
给我们留言
验证码