捕获并打印程序日志信息的方法(Qt)

工作中为了方便调试,常常需要加入一些打印。常用 Qt 中的 QDebug / QWarning,C 和 C++ 中的 printf / cout 等等,又或者是三方库提供的标准打印接口。
大部分时候,由于这些打印相当不统一(格式和位置),并且因为 Qt 作为 GUI 框架,调试信息实在不应该直接置于 UI 之上。
接下来介绍一种能统一和标准化所有标准打印的方法( 所谓标准打印即标准输出 stdout 等),并且能够动态配置。

关键函数

QT4下
typedef void (*QtMsgHandler)(QtMsgType, const char *);
Q_CORE_EXPORT QtMsgHandler qInstallMsgHandler(QtMsgHandler);
QT5下
typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);

对于 Qt 自身的打印,捕获起来比较容易,使用 qInstallMsgHandler 或 qInstallMessageHandler() 安装一个消息处理器,它指向一个函数,其函数签名如下第一行所示. 该函数能够捕获由 Qt Debug 产生的各种类型的打印消息,然后可以在此函数集中处理。

对于三方库,只要他是标准输出,我们就可以使用一些技巧来捕获它:
这里我们需要借助一个C库函数 freopen(),其声明如下:

FILE *freopen(const char * restrict filename, const char * restrict mode, FILE * restrict stream);

该函数用于重定向输入输出流。它可以在不改变代码原貌的情况下改变输入输出环境,但使用时应当保证流是可靠的。

样例

接下来实现一个完整的,能够处理所有情况的例子:

ShowInfo类
#include "showinfo.h"
#include <cstdio>

using namespace std;

ShowInfo::ShowInfo(QObject *parent) :
    QObject(parent),    watchedStdoutFile(qApp->applicationDirPath() + "/test" + "/stdout")
{
    edit  = new QTextEdit();
    stdoutFileDir = qApp->applicationDirPath() + "/test";
    lineCount = 0;
    fileSize = 0;
    initialized = false;
    count = 1;
    initialized = false;

    watcher = new QTimer(qApp);
}
  
void ShowInfo::myUpdate(){
    int len = watchedStdoutFile.size();
    if (len != fileSize) {
        fileSize = watchedStdoutFile.size();
        QString watchedMsg = QString::fromLocal8Bit(watchedStdoutFile.readAll());
        if (!watchedMsg.isEmpty()) {
            QStringList list = watchedMsg.split('\n');
            foreach (QString msg,list) {
                msg = msg.trimmed();
                QString time = QDateTime::currentDateTime().toString("[yyyy-MM-dd-hh:mm:ss:zzz] ");
                if (!msg.isEmpty()) msg = time + msg;
                edit->append(msg);
                if (!edit->textCursor().hasSelection()) edit->moveCursor(QTextCursor::End);
                if (++lineCount > 50000) {
                    lineCount = 0;
                    edit->clear();
                }
            }
        }
    }
    return;
}

void ShowInfo::initializeDebugEnveriment()
{
  if (!initialized) {
      qRegisterMetaType<QTextCursor>("QTextCursor");

      QPalette palette = edit->palette();
      palette.setBrush(QPalette::Highlight, QColor(0, 120, 230));
      edit->setPalette(palette);
      edit->setReadOnly(true);
      edit->setWindowTitle(QString("test窗口"));
      edit->setWindowFlags(Qt::WindowStaysOnTopHint);
      edit->resize(800, 500);
      edit->show();

      if (!QDir().exists(stdoutFileDir)) QDir().mkpath(stdoutFileDir);

      std::freopen((stdoutFileDir + "/stdout").toLocal8Bit().data(), "w", stdout);
      if (!watchedStdoutFile.open(QIODevice::ReadOnly | QIODevice::Text)){
          initialized = false;
          return;
      }

      connect(watcher,SIGNAL(timeout()), this, SLOT(myUpdate()));
      watcher->start(100);
      initialized = true;
  }
}

void ShowInfo::countPlus(){
    qDebug() << "This is Qt Debug message! Count:" << count++;
	qWarning() << "Warning: " << count++;
}

void ShowInfo::countPlus2(){
    std::printf("This is printf stdout message! Count: %d", count++);
    std::fflush(stdout);
}
main.cpp
ShowInfo * sInfo = NULL;

void outputMessage(QtMsgType type,   const char *msg)
{
    QString time = QDateTime::currentDateTime().toString("[yyyy-MM-dd-hh:mm:ss:zzz] ");
    sInfo->edit->append(time + QString(msg));
    if (!sInfo->edit->textCursor().hasSelection()) sInfo->edit->moveCursor(QTextCursor::End);
    if (++sInfo->lineCount > 50000) {
        sInfo->lineCount = 0;
        sInfo->edit->clear();
    }
}


int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

     sInfo = new ShowInfo();
     sInfo->initializeDebugEnveriment();
    //注册MessageHandler
     qInstallMsgHandler (outputMessage);

    QTimer timer;
    QObject::connect(&timer, SIGNAL(timeout()), sInfo, SLOT(countPlus()));
    timer.start(1000);

    QTimer otherTimer;
    QObject::connect(&otherTimer, SIGNAL(timeout()), sInfo, SLOT(countPlus2()));
    otherTimer.start(1500);

    return a.exec();
}
运行结果

在这里插入图片描述

结束

基于这个例子 ,日志信息就可以自由保存了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值