Qt:日志重定向窗口

1059 篇文章 284 订阅

相对于第三方的日志库,在 Qt 中使用 QDebug 打印更便捷,有时候也需要对 QDebug 输出进行重定向,如写入文件等。

在 Qt4 中使用 qInstallMsgHandler 函数设置重定向的函数指针:

typedef void (*QtMsgHandler)(QtMsgType, const char *);
Q_CORE_EXPORT QT_DEPRECATED QtMsgHandler qInstallMsgHandler(QtMsgHandler);

在 Qt5 中应该使用 qInstallMessageHandler 来注册函数指针:

typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);

返回的函数指针我们可以保存起来,需要输出到控制台时进行调用。

最简单的操作

一个最简单的示例如下,重定向到文件:

#include <QApplication>
#include <QMutex>
#include <QMutexLocker>
#include <QFile>
#include <QTextStream>
#include <QDebug>
 
//重定向qdebug输出到文件
void myMessageHandle(QtMsgType , const QMessageLogContext& , const QString& msg)
{
    static QMutex mut; //多线程打印时需要加锁
    QMutexLocker locker(&mut);
    QFile file("log.txt");
    if(file.open(QIODevice::WriteOnly|QIODevice::Append))
    {
        QTextStream stream(&file);
        stream<<msg<<endl;
        file.close();
    }
}
 
int main()
{
    //设置重定向操作的函数
    qInstallMessageHandler(myMessageHandle);
 
    qDebug()<<"Test debug111";
    qDebug()<<"Test debug222";
 
    return 0;
}

在这里插入图片描述

实现一个简单的日志管理类

需求很简单,同时输出到界面中的编辑框、文件、控制台。

代码链接:https://github.com/gongjianbo/SimpleQtLogger

运行效果:
在这里插入图片描述

在这里插入图片描述
输出到控制台,我是保存了调用 qInstallMessageHandler 时返回的函数指针,然后调用进行默认的输出。

输出到文件,因为函数调用发生在 qDebug() 调用的线程,所以需要加锁。

输出到界面,我使用了信号槽的方式,将文本发送给 connect 的槽。

当然,还可以添加一些细节,如日志等级的控制等。

下面是部分源码:

#include <QApplication>
 
#include "LogManager.h"
#include "mainwindow.h"
 
int main(int argc, char *argv[])
{
    LogManager::getInstance()->initManager();//初始化
 
    QApplication a(argc, argv);
 
    MainWindow w;
    w.show();
 
    return a.exec();
}
#ifndef LOGMANAGER_H
#define LOGMANAGER_H
 
#include <QObject>
#include <QFile>
#include <QMutex>
#include <QElapsedTimer>
#include <QDebug>
 
/**
 * @brief 简易的日志管理类,作为单例
 * @details
 * 初始化时调用initManager重定向
 * @note
 * 改为手动调用initManager是为了便于流程控制
 * 此外,也可以手动调用freeManager
 */
class LogManager : public QObject
{
    Q_OBJECT
    LogManager();
public:
    ~LogManager();
 
    //获取单例实例
    static LogManager *getInstance();
    //重定向qdebug输出
    void outputLog(QtMsgType type, const QMessageLogContext& context, const QString& msg);
    //初始化,如重定向等
    void initManager(const QString &path=QString());
    //释放
    void freeManager();
 
signals:
    //可以关联信号接收日志信息,如显示到ui中
    //注意,如果槽函数为lambda或者其他没有接收者的情况,需要保证槽函数中的变量有效性
    //因为static变量的生命周期更长,可能槽函数所在模块已经释放资源,最好connect加上接收者
    void newLog(int msgType, const QString &log);
 
private:
    //保留默认handle,用于输出到控制台
    QtMessageHandler defaultOutput = nullptr;
    //输出到文件
    QFile file;
    //输出路径
    QString filePath;
    //多线程操作时需要加锁
    QMutex logMutex;
    //计算操作间隔,分时生成文件
    QElapsedTimer elapsedTimer;
};
 
#endif // LOGMANAGER_H
#include "LogManager.h"
 
#include <QApplication>
#include <QDir>
#include <QThread>
#include <QTextStream>
#include <QDateTime>
 
//重定向qdebug输出
void outputLogMessage(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
    //转发给单例的成员函数
    LogManager::getInstance()->outputLog(type,context,msg);
}
 
LogManager::LogManager()
{
    //initManager();
}
 
LogManager::~LogManager()
{
    freeManager();
}
 
LogManager *LogManager::getInstance()
{
    //单例,c++ 11
    static LogManager instance;
    return &instance;
}
 
void LogManager::outputLog(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    //如果要写文件需要加锁,因为函数调用在debug调用线程
    QMutexLocker locker(&logMutex);
    QString out_text;
    QTextStream stream(&out_text);
    //区分日志类型给文本加头
    switch (type) {
    case QtDebugMsg: stream<<"[Debug]"; break;
    case QtInfoMsg: stream<<"[Info]"; break;
    case QtWarningMsg: stream<<"[Warning]"; break;
    case QtCriticalMsg: stream<<"[Critical]"; break;
    case QtFatalMsg: stream<<"[Fatal]"; break;
    default: stream<<"[Unknown]"; break;
    }
 
    //加个线程id用于测试
    stream<<QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss]")
            <<msg
         <<QThread::currentThreadId();
 
    //写入文件
    if(file.isOpen()){
        //elapsed距离start的毫秒数
        //这里设置1分钟用来测试
        if(elapsedTimer.elapsed()>1000*60){
            file.close();
            //重新计时
            elapsedTimer.restart();
        }
    }
    if(!file.isOpen()){
        //新的文件
        file.setFileName(filePath+QString("/log_%1.txt")
                         .arg(QDateTime::currentDateTime().toString("yyyyMMdd_hhmm")));
        //Append追加模式,避免同一文件被清除
        if(!file.open(QIODevice::WriteOnly|QIODevice::Append)){
            emit newLog(QtWarningMsg,"Open log file error:"+file.errorString()+file.fileName());
        }
    }
    if(file.isOpen()){
        //写入文件
        stream.setDevice(&file);
        stream<<out_text<<endl;
    }
 
    //发送信号给需要的对象,如ui上显示日志
    emit newLog(type, msg);
 
    //默认的输出,控制台
    //区分日志类型给文本加颜色
    //常见格式为:\e[显示方式;前景色;背景色m输出字符串\e[0m
    //其中\e=\033
    QString cmd_text;
    stream.setString(&cmd_text);
    switch (type) {
    //debug绿色
    case QtDebugMsg: stream<<"\033[32m"; break;
        //info蓝色
    case QtInfoMsg: stream<<"\033[34m"; break;
        //warning黄色
    case QtWarningMsg: stream<<"\033[33m"; break;
        //critical黑底白字
    case QtCriticalMsg: stream<<"\033[0;37;40m"; break;
        //fatal黑底红字
        //qFatal表示致命错误,默认处理会报异常的
    case QtFatalMsg: stream<<"\033[0;31;40m"; break;
        //defualt默认颜色
    default: stream<<"\033[0m"; break;
    }
    stream<<out_text<<"\033[0m";
    defaultOutput(type,context,cmd_text);
}
 
void LogManager::initManager(const QString &path)
{
    //保存路径
    filePath=path;
    if(filePath.isEmpty())
    {
        //用到了QApplication::applicationDirPath(),需要先实例化一个app
        int argc=0;
        QApplication app(argc,nullptr);
        filePath=app.applicationDirPath()+"/log";
    }
    QDir dir(filePath);
    if(!dir.exists())
    {
        //虽然QFile能够创建不存在的文件,但是它就是不会自动创建不存在的目录
        dir.mkpath(filePath);
    }
    elapsedTimer.start();
    //重定向qdebug到自定义函数
    defaultOutput=qInstallMessageHandler(outputLogMessage);
}
 
void LogManager::freeManager()
{
    file.close();
    qInstallMessageHandler(nullptr);
}
 

日志重定向到窗口(不要看)

封装类

1、logbrowser.h

#ifndef LOG_BROWSER_H
#define LOG_BROWSER_H

#include <QWidget>
#include <QVBoxLayout>
#include <QTextBrowser>
#include <QPushButton>
#include <QCoreApplication>
#include <QDebug>
#include <QMessageBox>
#include <QCloseEvent>
#include <QKeyEvent>

class LogBrowser : public QWidget
{
    Q_OBJECT
    bool is_finished;

    QTextBrowser *browser;
    QPushButton * start_button ;
    QPushButton * clear_button;
public:
    explicit LogBrowser(QWidget *parent = nullptr);
    void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg);
    void closeEvent(QCloseEvent *event);
    void keyPressEvent(QKeyEvent *event);
public:
    void start();
signals:

};

#endif // LOG_BROWSER_H

2、logbrowser.cpp

#include "logbrowser.h"

#include <QLoggingCategory>


Q_DECLARE_LOGGING_CATEGORY(testLogger)
Q_LOGGING_CATEGORY(testLogger, "ssh.client", QtDebugMsg)

LogBrowser::LogBrowser(QWidget *parent) : QWidget(parent)
{
    this->resize(400, 300);
    is_finished = false;

    browser = new QTextBrowser();
    start_button = new QPushButton();
    clear_button = new QPushButton();

    start_button->setText("start");
    clear_button->setText("clear");

    QHBoxLayout *button_layout = new QHBoxLayout();
    button_layout->addStretch();
    button_layout->addWidget(start_button);
    button_layout->addWidget(clear_button);
    button_layout->setSpacing(10);
    button_layout->setContentsMargins(0, 0, 10, 10);

    QVBoxLayout *main_layout = new QVBoxLayout();
    main_layout->addWidget(browser);
    main_layout->addLayout(button_layout);
    main_layout->setSpacing(10);
    main_layout->setContentsMargins(0, 0, 0, 0);

    this->setLayout(main_layout);
    connect(start_button, &QPushButton::clicked, this, &LogBrowser::start);
    connect(clear_button, &QPushButton::clicked, browser, &QTextBrowser::clear);
}


void LogBrowser::outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QString message;
    switch(type)
    {
        case QtDebugMsg:
        message = QString("Debug:");
        break;

        case QtWarningMsg:
        message = QString("Warning:");
        break;

        case QtCriticalMsg:
        message = QString("Critical:");
        break;

        case QtFatalMsg:
        message = QString("Fatal:");
    }

    browser->append(message.append(msg));
}


void LogBrowser::start()
{
    if(!is_finished)
    {
        for(int i=0; i<1000000; i++)
        {
            QCoreApplication::processEvents();
            qCDebug(testLogger)   << ": test ";
            qDebug()<<QString("This is a Qt log browser").append(QString::number(i, 10));

        }
        is_finished = true;
    }
}

void LogBrowser::closeEvent(QCloseEvent *event)
{
    QMessageBox::StandardButton answer = QMessageBox::question(
        this,
        tr("Close Log Browser?"),
        tr("Do you really want to close the log browser?"),
        QMessageBox::Yes | QMessageBox::No
    );

    if (answer == QMessageBox::Yes)
        event->accept();
    else
        event->ignore();
}

void LogBrowser::keyPressEvent(QKeyEvent *event)
{
    event->ignore();
}

使用

1、main.cpp

#include "mainwindow.h"
#include <QPointer>
#include <QApplication>


LogBrowser *mylog;
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    if(mylog)
        mylog->outputMessage(type, context, msg);
}

//void test(){
//    for(int i=0; i<20; i++)
//    {
//        QCoreApplication::processEvents();
//        qDebug()<<QString("This is a Qt log browser  tetststststts").append(QString::number(i, 10));

//    }
//}
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    mylog = new LogBrowser();
    mylog->show();
    qInstallMessageHandler(outputMessage);
//    test();
    return a.exec();
}

在这里插入图片描述
2、使用2:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <logbrowser.h>

LogBrowser *mylog;
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    if(mylog)
        mylog->outputMessage(type, context, msg);
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    mylog = new LogBrowser(this);
    mylog->show();
    qInstallMessageHandler(outputMessage);



    //--------------------------------
    for(int i=0; i<20; i++)
    {
        QCoreApplication::processEvents();
        qDebug()<<QString("This is a Qt log browser  tetststststts").append(QString::number(i, 10));

    }
}

在这里插入图片描述

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值