千家信息网

Log4Qt快速入门——Log4Qt日志输出重定向源码解析

发表于:2025-01-31 作者:千家信息网编辑
千家信息网最后更新 2025年01月31日,Log4Qt快速入门--Log4Qt日志输出重定向源码解析一、Appender简介1、Appender简介Appender是所有Appender的抽象类,是对记录日志形式的抽象。Log4Qt(Qt4版
千家信息网最后更新 2025年01月31日Log4Qt快速入门——Log4Qt日志输出重定向源码解析

Log4Qt快速入门--Log4Qt日志输出重定向源码解析

一、Appender简介

1、Appender简介

Appender是所有Appender的抽象类,是对记录日志形式的抽象。Log4Qt(Qt4版本)中Appender继承体系如下:

2、Appender接口

virtual Filter *filter() const = 0;virtual QString name() const = 0;virtual Layout *layout() const = 0;virtual bool requiresLayout() const = 0;virtual void setLayout(Layout *pLayout) = 0;virtual void setName(const QString &rName) = 0;virtual void addFilter(Filter *pFilter) = 0;virtual void clearFilters() = 0; virtual void close() = 0;virtual void doAppend(const LoggingEvent &rEvent) = 0;

二、AppenderSkeleton

1、AppenderSkeleton简介

AppenderSkeleton继承自Appender类,实现了Appender的通用功能,但没有实现继承自Appender的部分接口,所以仍然是一个抽象类,不能实例化。AppenderSkeleton的所有函数都是线程安全的。

2、AppenderSkeleton接口

virtual Filter *filter() const;virtual Layout *layout() const;bool isActive() const;bool isClosed() const;virtual QString name() const;Level threshold() const;virtual void setLayout(Layout *pLayout);virtual void setName(const QString &rName);void setThreshold(Level level);virtual void activateOptions();virtual void addFilter(Filter *pFilter);virtual void clearFilters();virtual void close();virtual void doAppend(const LoggingEvent &rEvent);Filter* firstFilter() const;bool isAsSevereAsThreshold(Level level) const;

自定义Appender可以从AppenderSkeleton派生,须要实现以下三个接口:

virtual void append(const LoggingEvent &rEvent) = 0;virtual bool requiresLayout() const = 0;virtual QDebug debug(QDebug &rDebug) const = 0;

append接口负责处理LoggingEvent对象,将格式化的日志信息输出到不同的输出地,如文本流、文件流、数据库等,如果需要将日志信息重定向到QWidget组件,需要在append函数发送信号,日志信息作为信号参数,在相应的QWidget组件的槽函数接收处理日志信息。
也可以根据需要从WriterAppender、ConsoleAppender、
FileAppender、RollingFileAppender、DailyRollingFileAppender派生类进行实现。

三、WriterAppender

1、WriterAppender简介

WriterAppender类继承自AppenderSkeleton类,在其实现的append函数中会将LoggingEvent对象的日志信息输出到QTextStream对象。WriterAppender的所有函数是线程安全的。

void WriterAppender::append(const LoggingEvent &rEvent){    QString message(layout()->format(rEvent));    //输出格式化的日志信息到QTextStream对象    *mpWriter << message;    if (handleIoErrors())        return;    // 是否刷新    if (immediateFlush())    {        mpWriter->flush();        if (handleIoErrors())            return;    }}

2、WriterAppender常用接口

QTextCodec *encoding() const;
获取输出文本流的编码器
bool immediateFlush() const;
获取是否立即刷新
QTextStream *writer() const;
获取输出文本流对象
void setEncoding(QTextCodec *pTextCodec);
设置文本流的编码器
void setImmediateFlush(bool immediateFlush);
设置是否立即刷新
void setWriter(QTextStream *pTextStream);
设置输出文本流
virtual void close();
关闭文本流,如果有设置页脚,会打印出页脚信息

四、ConsoleAppender

1、ConsoleAppender简介

ConsoleAppender类继承自WriterAppender类,ConsoleAppender定义了标准输出、标准错误两种控制台的输出目的地。ConsoleAppender的所有函数是线程安全的。

enum Target{    STDOUT_TARGET,//标准输出    STDERR_TARGET//标准错误};

2、ConsoleAppender日志重定向的实现

在ConsoleAppender配置完成后,需要对其配置选项进行激活,ConsoleAppender的activateOptions函数中会将文本流指向相应控制台的文本流对象。

void ConsoleAppender::activateOptions(){    QMutexLocker locker(&mObjectGuard);    closeStream();    if (mTarget == STDOUT_TARGET)        mpTextStream = new QTextStream(stdout);    else        mpTextStream = new QTextStream(stderr);    // 调用WriterAppender的setWriter函数,    // 将日志信息重定向到控制台对应的文本流    setWriter(mpTextStream);    WriterAppender::activateOptions();}

当Logger进行日志输出时,会在WriterAppender的append函数将格式化的日志信息输出到相应控制台对应的文本流,完成输出目的地的重定向。

3、ConsoleAppender常用接口

QString target() const;
获取输出目的地
void setTarget(const QString &rTarget);
设置rTarget字符串为输出目的地
void setTarget(Target target);
设置target为输出目的地
virtual void activateOptions();
激活ConsoleAppender设置的选项
virtual void close();
关闭ConsoleAppender

4、ConsoleAppender示例

#include #include #include #include #include #include #include int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QThread::currentThread()->setObjectName("MainThread");    // 创建TTCCLayout    Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger();    Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout();    layout->setDateFormat("yyyy-mm-dd hh:mm:ss");    // 激活选项    layout->activateOptions();    // 创建ConsoleAppender    Log4Qt::ConsoleAppender *appender = new Log4Qt::ConsoleAppender();    appender->setLayout(layout);    // 设置编码    appender->setEncoding(QTextCodec::codecForName("UTF-8"));    // 设置输出目的地为stdout    appender->setTarget(Log4Qt::ConsoleAppender::STDOUT_TARGET);    appender->setImmediateFlush(true);    // 设置阈值级别为INFO    appender->setThreshold(Log4Qt::Level::INFO_INT);    // 激活选项    appender->activateOptions();    logger->addAppender(appender);    // 设置级别为 DEBUG    logger->setLevel(Log4Qt::Level::DEBUG_INT);    // 输出信息    logger->debug("你好, Log4Qt!");    logger->info("你好, Qt!");    // 关闭 logger    logger->removeAllAppenders();    logger->loggerRepository()->shutdown();    return a.exec();}// output:// 2018-03-12 18:03:48 [MainThread] INFO  root  - 你好, Qt!

五、FileAppender

1、FileAppender简介

FileAppender类继承自WriterAppender类,用于将日志输出到文件。
FileAppender的所有函数是线程安全的。

2、FileAppender日志重定向的实现

在FileAppender配置完成后,需要对其配置选项进行激活,FileAppender的activateOptions函数中会打开指定的输出文件,并将WriterAppender的文本流绑定到输出文件的文件流,实现将WriterAppender的文本流重定向到输出文件中。

void FileAppender::activateOptions(){    QMutexLocker locker(&mObjectGuard);    if (mFileName.isEmpty())    {        LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Activation of Appender '%1' that requires file and has no file set"),                                         APPENDER_ACTIVATE_MISSING_FILE_ERROR);        e << name();        logger()->error(e);        return;    }    closeFile();    // 打开文件    openFile();    WriterAppender::activateOptions();}void FileAppender::openFile(){    Q_ASSERT_X(mpFile == 0 && mpTextStream == 0, "FileAppender::openFile()", "Opening file without closing previous file");    QFileInfo file_info(mFileName);    QDir parent_dir = file_info.dir();    if (!parent_dir.exists())    {        logger()->trace("Creating missing parent directory for file %1", mFileName);        QString name = parent_dir.dirName();        parent_dir.cdUp();        parent_dir.mkdir(name);    }    mpFile = new QFile(mFileName);    QFile::OpenMode mode = QIODevice::WriteOnly | QIODevice::Text;    //配置文件流的写入模式    if (mAppendFile)        mode |= QIODevice::Append;    else        mode |= QIODevice::Truncate;    if (!mBufferedIo)        mode |= QIODevice::Unbuffered;    if (!mpFile->open(mode))    {        LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Unable to open file '%1' for appender '%2'"),                                         APPENDER_OPENING_FILE_ERROR);        e << mFileName << name();        e.addCausingError(LogError(mpFile->errorString(), mpFile->error()));        logger()->error(e);        return;    }    // 将文件流绑定文本流    mpTextStream = new QTextStream(mpFile);    // 将WriterAppender的文本流重定向到文本文件的文件流对应的文本流    // 完成输出目的地的重定向    setWriter(mpTextStream);    logger()->debug("Opened file '%1' for appender '%2'", mpFile->fileName(), name());}

当Logger进行日志输出时,会在WriterAppender的append函数将格式化的日志信息输出到文本流绑定的输出文件中,完成输出目的地的重定向。

3、FileAppender接口

bool appendFile() const;
获取是否追加文件
QString file() const;
获取输出目的地的文件名
bool bufferedIo() const;
获取是否为缓存IO
void setAppendFile(bool append);
设置是否为追加文件
void setBufferedIo(bool buffered);
设置是否为缓存IO
void setFile(const QString &rFileName);
设置输出目的地的文件名
virtual void close();
关闭文件
4、FileAppender示例

#include #include #include #include #include .h>#include #include int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QThread::currentThread()->setObjectName("MainThread");    // 创建TTCCLayout    Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger();    Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout();    layout->setDateFormat("yyyy-mm-dd hh:mm:ss");    // 激活选项    layout->activateOptions();    // 创建ConsoleAppender    Log4Qt::FileAppender *appender = new Log4Qt::FileAppender;    // 设置输出目的地为应用程序所在目录下的logFile.log    appender->setFile("logFile.log");    appender->setLayout(layout);    // 设置编码    appender->setEncoding(QTextCodec::codecForName("UTF-8"));    appender->setImmediateFlush(true);    // 设置阈值级别为INFO    appender->setThreshold(Log4Qt::Level::INFO_INT);    // 激活选项    appender->activateOptions();    logger->addAppender(appender);    // 设置级别为 DEBUG    logger->setLevel(Log4Qt::Level::DEBUG_INT);    // 输出信息    logger->debug("你好, Log4Qt!");    logger->info("你好, Qt!");    // 关闭 logger    logger->removeAllAppenders();    logger->loggerRepository()->shutdown();    return a.exec();}// logFile:// 2018-06-12 18:06:45 [MainThread] INFO  root  - 你好, Qt!

六、RollingFileAppender

1、RollingFileAppender简介

RollingFileAppender类继承自FileAppender类,是对FileAppender功能的扩展。RollingFileAppender允许输出的日志文件达到指定大小时进行日志文件的滚动备份。
RollingFileAppender的所有函数都是线程安全的。

2、RollingFileAppender日志重定向的实现

在RollingFileAppender配置完成后,需要对其配置选项进行激活,RollingFileAppender调用FileAppender::activateOptions函数中会中会打开指定的输出文件,并将WriterAppender的文本流绑定到输出文件的文件流,实现将WriterAppender的文本流重定向到输出文件中。
RollingFileAppender实现了append函数。

void RollingFileAppender::append(const LoggingEvent &rEvent){    // Q_ASSERT_X(, "RollingFileAppender::append()", "Lock must be held by caller")    // 使用FileAppender将输出文件绑定到WidgetAppender的输出文本流    FileAppender::append(rEvent);    // 如果日志文件大小已经大于日志文件指定的最大值,进行会滚备份操作    if (writer()->device()->size() > this->mMaximumFileSize)        rollOver();}

当Logger进行日志输出时,RollingFileAppender使用FileAppender::append(实际为WriterAppender::append)函数将将格式化的日志信息输出到文本流绑定的输出文件中,完成输出目的地的重定向。如果输出文件的大小大于指定的最大值时,进行会滚备份操作。

3、RollingFileAppender接口

int maxBackupIndex() const;
获取最大备份索引
qint64 maximumFileSize() const;
获取输出日志文件大小的最大值
void setMaxBackupIndex(int maxBackupIndex);
设置备份文件索引的最大值
void setMaximumFileSize(qint64 maximumFileSize);
设置单个输出日志文件的最大值为maximumFileSize字节
void setMaxFileSize(const QString &rMaxFileSize);
设置单个输出日志文件的最大值为rMaxFileSize的值,可以使用KB,MB,GB等单位

4、RollingFileAppender示例

#include #include #include #include #include .h>.h>#include #include int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QThread::currentThread()->setObjectName("MainThread");    // 创建TTCCLayout    Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger();    Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout();    layout->setDateFormat("yyyy-mm-dd hh:mm:ss");    // 激活选项    layout->activateOptions();    // 创建ConsoleAppender    Log4Qt::RollingFileAppender *appender = new Log4Qt::RollingFileAppender;    // 设置输出目的地为应用程序所在目录下的logFile.log    appender->setFile("logFile.log");    // 设置日志为追加方式写入输出文件    appender->setAppendFile(true);    // 设置备份文件的最大数量为10个    appender->setMaxBackupIndex(10);    // 设置输出文件的最大值为1KB    appender->setMaxFileSize("1KB");    appender->setLayout(layout);    // 设置编码    appender->setEncoding(QTextCodec::codecForName("UTF-8"));    appender->setImmediateFlush(true);    // 设置阈值级别为INFO    appender->setThreshold(Log4Qt::Level::INFO_INT);    // 激活选项    appender->activateOptions();    logger->addAppender(appender);    // 设置级别为 DEBUG    logger->setLevel(Log4Qt::Level::DEBUG_INT);    // 输出信息    for(int i = 0 ; i < 100; i++)    {        logger->debug("你好, Log4Qt!");        logger->info("你好, Qt!");    }    // 关闭 logger    logger->removeAllAppenders();    logger->loggerRepository()->shutdown();    return a.exec();}

程序最近的日志输出到logFile.log,并备份有5个文件,分别为logFile.log.1、logFile.log.2、logFile.log.3、logFile.log.4、logFile.log.5

七、DailyRollingFileAppender

1、DailyRollingFileAppender简介

DailyRollingFileAppender类继承自FileAppender类,是对FileAppender功能的扩展。DailyRollingFileAppender允许输出的日志文件按照指定的频率进行会滚备份。
DailyRollingFileAppender的所有函数都是线程安全的。
DailyRollingFileAppender定义了六种日期模式。

enum DatePattern{    MINUTELY_ROLLOVER = 0,// 每分钟,'yyyy-MM-dd-hh-mm"    HOURLY_ROLLOVER,// 每小时,yyyy-MM-dd-hh    HALFDAILY_ROLLOVER,// 每半天,yyyy-MM-dd-a    DAILY_ROLLOVER,// 每天,yyyy-MM-dd    WEEKLY_ROLLOVER,// 每周,yyyy-ww    MONTHLY_ROLLOVER// 每月,yyyy-MM};

2、DailyRollingFileAppender日志重定向的实现

在DailyRollingFileAppender配置完成后,需要对其配置选项进行激活,DailyRollingFileAppender的activateOptions函数中会计算输出文件回滚的频率,并调用FileAppender::activateOptions()函数将WriterAppender的文本流绑定到输出文件的文件流,实现将WriterAppender的文本流重定向到输出文件中。

void DailyRollingFileAppender::activateOptions(){    QMutexLocker locker(&mObjectGuard);    // 计算输出文件回滚的频率,    computeFrequency();    if (!mActiveDatePattern.isEmpty())    {        //计算输出文件回滚的时间        computeRollOverTime();        // 调用FileAppender::activateOptions重定向输出文本流到输出文件        FileAppender::activateOptions();    }}

DailyRollingFileAppender实现了append函数。

void DailyRollingFileAppender::append(const LoggingEvent &rEvent){    // 如果当前时间大于输出文件回滚时间,进行输出文件回滚    if (QDateTime::currentDateTime() > mRollOverTime)        rollOver();    // 调用FileAppender::append将格式化的日志输出到输文件    FileAppender::append(rEvent);}

当Logger进行日志输出时,DailyRollingFileAppender会在append函数内处理LoggingEvent对象。如果当前时间大于输出文件需要进行回滚的时间,DailyRollingFileAppender会进行输出文件的回滚备份操作,创建新的日志输出文件。然后调用FileAppender::append函数将格式化的日志输出到指定的输出文件中。

3、DailyRollingFileAppender接口

QString datePattern() const;
获取日期匹配模式字符串
void setDatePattern(DatePattern datePattern);
设置日期匹配模式
void setDatePattern(const QString &rDatePattern);
设置rDatePattern为日期匹配模式

4、DailyRollingFileAppender示例

#include #include #include #include #include >#include #include int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QThread::currentThread()->setObjectName("MainThread");    // 创建TTCCLayout    Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger();    Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout();    layout->setDateFormat("yyyy-mm-dd hh:mm:ss");    // 激活选项    layout->activateOptions();    // 创建ConsoleAppender    Log4Qt::DailyRollingFileAppender *appender = new Log4Qt::DailyRollingFileAppender;    // 设置输出目的地为应用程序所在目录下的logFile.log    appender->setFile("logFile.log");    // 设置日志文件每天回滚    //appender->setDatePattern(Log4Qt::DailyRollingFileAppender::MINUTELY_ROLLOVER);    appender->setDatePattern("'.'yyyy-MM-dd-hh-mm");    // 设置日志为追加方式写入输出文件    appender->setAppendFile(true);    appender->setLayout(layout);    // 设置编码    appender->setEncoding(QTextCodec::codecForName("UTF-8"));    appender->setImmediateFlush(true);    // 设置阈值级别为INFO    appender->setThreshold(Log4Qt::Level::INFO_INT);    // 激活选项    appender->activateOptions();    logger->addAppender(appender);    // 设置级别为 DEBUG    logger->setLevel(Log4Qt::Level::DEBUG_INT);    // 输出信息    for(int i = 0; i < 10; i++)    {        logger->debug("你好, Log4Qt!");        logger->info("你好, Qt!");        for(int i = 0; i < 1000000000; i++)            ;        logger->debug("你好, Log4Qt2!");        logger->info("你好, Qt2!");    }    // 关闭 logger    logger->removeAllAppenders();    logger->loggerRepository()->shutdown();    return a.exec();}

程序执行时,日志输出到logFile.log,输出日志每分钟会回滚备份一次,备份文件名称如下:logFile.log.2018-10-12-18-30。

八、重定向日志到QWidget

1、Log4Qt源码导入

将Log4Qt源码工程下的src/log4qt目录拷贝到自己的工程中,并在自己的工程文件中添加如下配置:

# 定义所需的宏DEFINES += LOG4QT_LIBRARY# 将Log4Qt源码工程下的src/log4qt目录拷贝到自己的工程src目录中# 定义Log4Qt源码根目录LOG4QT_ROOT_PATH = $$PWD/log4qt# 指定编译项目时应该被搜索的#include目录INCLUDEPATH += $$LOG4QT_ROOT_PATH# 将Log4Qt源代码添加至项目中include($$LOG4QT_ROOT_PATH/log4qt.pri)

2、WidgetAppender实现

WidgetAppender从AppenderSkeleton进行实现。
WidgetAppender.h文件:

#ifndef WIDGETAPPENDER_H#define WIDGETAPPENDER_H#include #include #include "appenderskeleton.h"#include namespace Log4Qt{/** * @brief WidgetAppender继承自AppenderSkeleton类,用于将日志信息重定向到QWidget组件 * @author scorpio * @note WidgetAppender的使用注意事项: *      1、必须使用setLogWidget接口设置日志信息的重定向位置,即输出窗口组件 *      2、必须实现一个槽函数onAppendLog(const QString& msg),用于接收WidgetAppender *      发送的logAppend(const QString& msg)信号,参数msg即接收的日志信息,输出窗口组件 *      需要将msg输出到相应窗体。 */class WidgetAppender : public AppenderSkeleton{    Q_OBJECTpublic:    WidgetAppender(QObject *parent = NULL);    ~WidgetAppender();    /**     * @brief 设置日志信息输出的QWidget组件     * @param widget,输入参数,日志信息输出窗口,需要开发者自己实现     */    void setLogWidget(const QWidget& widget);signals:    /**     * @brief 新增加一条日志信息的信号     * @param msg,输入参数,格式化后的日志信息     * @note logAppend信号由QWidget输出窗口组件接收,开发者需要在输出窗口组件实现     *      槽函数onAppendLog(const QString& msg)。     */    void logAppend(const QString& msg);protected:    virtual bool requiresLayout() const;    virtual void append(const Log4Qt::LoggingEvent &rEvent);#ifndef QT_NO_DEBUG_STREAM    virtual QDebug debug(QDebug &rDebug) const;#endif //QT_NO_DEBUG_STREAMprivate:    QWidget *m_logWidget;};}#endif // WIDGETAPPENDER_H

WidgetAppender.cpp文件:

#include "WidgetAppender.h"#include "loggingevent.h"#include namespace Log4Qt{WidgetAppender::WidgetAppender(QObject *parent): AppenderSkeleton(parent){    m_logWidget = NULL;}WidgetAppender::~WidgetAppender(){}void WidgetAppender::setLogWidget(const QWidget &widget){    m_logWidget = const_cast(&widget);    //连接槽函数到输出窗口的onAppendLog(const QString&)槽函数    connect(this, SIGNAL(logAppend(const QString&)), m_logWidget, SLOT(onAppendLog(const QString&)));}bool WidgetAppender::requiresLayout() const{    return true;}void WidgetAppender::append(const LoggingEvent &rEvent){    // 格式化日志信息    QString message = dynamic_cast(layout())->format(rEvent);    emit logAppend(message);}#ifndef QT_NO_DEBUG_STREAMQDebug WidgetAppender::debug(QDebug &rDebug) const{    return rDebug.space();}#endif //QT_NO_DEBUG_STREAM}

3、QWidget输出窗口实现

Widget.h文件:

#ifndef WIDGET_H#define WIDGET_H#include #include #include #include class Widget : public QWidget{    Q_OBJECTpublic:    Widget(QWidget *parent = 0);    ~Widget();private slots:    /**     * @brief 接收日志信息的槽函数     * @param log,输入参数,格式化后的日志信息     */    void onAppendLog(const QString& log);private:    QTextEdit* m_logEdit;    QMutex m_mutex;};#endif // WIDGET_H

Widget.cpp文件:

#include "Widget.h"Widget::Widget(QWidget *parent)    : QWidget(parent){    m_logEdit = new QTextEdit();    QVBoxLayout* layout = new QVBoxLayout;    layout->addWidget(m_logEdit);    setLayout(layout);    resize(600, 400);}Widget::~Widget(){}void Widget::onAppendLog(const QString &log){    QMutexLocker lock(&m_mutex);    //将日志信息输出到窗口组件    m_logEdit->insertPlainText(log);}

4、应用示例

#include "Widget.h"#include #include #include #include #include #include int main(int argc, char *argv[]){    QApplication a(argc, argv);    Widget w;    w.show();    Log4Qt::BasicConfigurator::configure();    Log4Qt::LogManager::setHandleQtMessages(true);    Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger();    logger->removeAllAppenders();    Log4Qt::WidgetAppender *appender = new Log4Qt::WidgetAppender();    appender->setName("WidgetAppender");    Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout(Log4Qt::TTCCLayout::ISO8601);    layout->setThreadPrinting(true);    appender->setLayout(layout);    appender->activateOptions();    //设置日志信息输出的窗口组件    appender->setLogWidget(w);    logger->addAppender(appender);    logger->warn("hello Log4Qt");    logger->info("hello Log4Qt");    logger->debug("hello Log4Qt");    logger->info("你好 Log4Qt");    logger->removeAllAppenders();    return a.exec();}// output:// 2018-10-09 10:27:18.542 [] WARN  root  - hello Log4Qt// 2018-10-09 10:27:18.542 [] INFO  root  - hello Log4Qt// 2018-10-09 10:27:18.542 [] DEBUG root  - hello Log4Qt// 2018-10-09 10:27:18.542 [] INFO  root  - 你好 Log4Qt
0