Qt日志输出类QtMessageHandler

1. 打开QT帮助文档,查找这个类可以看到有关这个类的描述

typedef <QtGlobal>::QtMessageHandler
This is a typedef for a pointer to a function with the following signature:

  void myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &);

显然,它指向的是一个函数

2. 看以下它的入口参数都是什么意思

enum <QtGlobal>::QtMsgType
This enum describes the messages that can be sent to a message handler (QtMessageHandler). You can use the enum to identify and associate the various message types with the appropriate actions.

Constant                  Value      Description
<QtGlobal>::QtDebugMsg      0        A message generated by the qDebug() function.
<QtGlobal>::QtInfoMsg       4        A message generated by the qInfo() function.
<QtGlobal>::QtWarningMsg    1        A message generated by the qWarning() function.
<QtGlobal>::QtCriticalMsg   2        A message generated by the qCritical() function.
<QtGlobal>::QtFatalMsg      3        A message generated by the qFatal() function.
<QtGlobal>::QtSystemMsg     QtCriticalMsg
 

QtMsgType这是一个枚举类型,它列举了六种消息

  • qDebug:调试信息
  • qInfo : 正常信息
  • qWarning:警告信息
  • qCritical:严重错误
  • qFatal:致命错误

再看一下QMessageLogContext

Detailed Description
The class provides information about the source code location a qDebug(), qInfo(), qWarning(), qCritical() or qFatal() message was generated.
Note: By default, this information is recorded only in debug builds. You can overwrite this explicitly by defining QT_MESSAGELOGCONTEXT or QT_NO_MESSAGELOGCONTEXT.
See also QMessageLogger, QtMessageHandler, and qInstallMessageHandler(). 

这里有个重要的信息:默认情况下,这些信息只记录在调试构建中。您可以通过定义QT_MESSAGELOGCONTEXT或QT_NO_MESSAGELOGCONTEXT来显式地覆盖它

这个就是当你要用Release构建的时候需要在pro文件中添加 

DEFINES += QT_MESSAGELOGCONTEXT

3. 下面还有一个相关的函数qInstallMessageHandler().

看下描述

QtMessageHandler <QtGlobal>::qInstallMessageHandler(QtMessageHandler handler)
Installs a Qt message handler which has been defined previously. Returns a pointer to the previous message handler.
The message handler is a function that prints out debug messages, warnings, critical and fatal error messages. The Qt library (debug mode) contains hundreds of warning messages that are printed when internal errors (usually invalid function arguments) occur. Qt built in release mode also contains such warnings unless QT_NO_WARNING_OUTPUT and/or QT_NO_DEBUG_OUTPUT have been set during compilation. If you implement your own message handler, you get total control of these messages.
The default message handler prints the message to the standard output under X11 or to the debugger under Windows. If it is a fatal message, the application aborts immediately.
Only one message handler can be defined, since this is usually done on an application-wide basis to control debug output.
To restore the message handler, call qInstallMessageHandler(0).

翻译一下

安装以前定义的Qt消息处理程序。返回指向前一个消息处理程序的指针。
消息处理程序是输出调试消息、警告、关键错误和致命错误消息的函数。Qt库(调试模式)包含数百条警告消息,当内部错误(通常是无效的函数参数)发生时,将打印这些消息。除非QT_NO_WARNING_OUTPUT和/或QT_NO_DEBUG_OUTPUT在编译期间被设置,否则在发布模式中构建的Qt也会包含这样的警告。如果您实现了自己的消息处理程序,则可以完全控制这些消息。
默认消息处理程序将消息打印到X11下的标准输出或Windows下的调试器。如果是致命消息,应用程序将立即中止。
只能定义一个消息处理程序,因为这通常是在应用程序范围内进行的,目的是控制调试输出。
要恢复消息处理程序,请调用qInstallMessageHandler(0)。

从这个描述中可以知道,我们需要定义一个函数,官方已经给出了示例:

void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
  {
      QByteArray localMsg = msg.toLocal8Bit();
      const char *file = context.file ? context.file : "";
      const char *function = context.function ? context.function : "";
      switch (type) {
      case QtDebugMsg:
          fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
          break;
      case QtInfoMsg:
          fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
          break;
      case QtWarningMsg:
          fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
          break;
      case QtCriticalMsg:
          fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
          break;
      case QtFatalMsg:
          fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
          break;
      }
  }

这个函数就是QtMessageHandler所指向的函数,其中type是指消息类型, context是保存产生消息的文件,函数,行数灯信息的集合,msg是消息

使用它的时候需要用qInstallMessageHandler(QtMessageHandler handler)去注册

qInstallMessageHandler(myMessageOutput);

4. 现在新建一个工程来调试一下这个函数的作用

在头文件中声明一个函数

static void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);

在cpp中去添加定义

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qInstallMessageHandler(myMessageOutput);

    qDebug() << "Debug";
    qWarning() << "Warning";
    qInfo() << "Info";
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    QByteArray localMsg = msg.toLocal8Bit();
    const char *file = context.file ? context.file : "";
    const char *function = context.function ? context.function : "";
    switch (type) {
    case QtDebugMsg:
        fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
        break;
    case QtInfoMsg:
        fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
        break;
    case QtWarningMsg:
        fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
        break;
    case QtCriticalMsg:
        fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
        break;
    case QtFatalMsg:
        fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), file, context.line, function);
        break;
    }
}

运行,什么都没有

关闭程序,打印出了一些信息

Debug: Debug (..\log\mainwindow.cpp:11, MainWindow::MainWindow(QWidget*))
Warning: Warning (..\log\mainwindow.cpp:12, MainWindow::MainWindow(QWidget*))
Info: Info (..\log\mainwindow.cpp:13, MainWindow::MainWindow(QWidget*))

可以看到,使用Qt日志可以准确定位到消息的位置,但是这个是关闭程序后才出现的,如果能实时的输出这种信息,调试程序的时候不就更加直观了吗

想起C语言中有关调试的宏

__FILE__: 所在文件名
__FUNCTION__:所在函数名
__LINE__:所在行数
__DATE__:执行日期
__TIME__:执行时间

那么在Qt中呢,百度了一下,确实是有的,和C一样的用法

比如

    qInstallMessageHandler(myMessageOutput);
    qInstallMessageHandler(nullptr);
    qDebug() << "[" <<__FILE__ <<":"<<__FUNCTION__<<":"<<__LINE__ <<"]" << "Debug";
    qWarning() << "[" <<__FILE__ <<":"<<__FUNCTION__<<":"<<__LINE__ <<"]" << "Warning";
    qInfo() << "[" <<__FILE__ <<":"<<__FUNCTION__<<":"<<__LINE__ <<"]" << "Info";

输出

[ ..\log\mainwindow.cpp : MainWindow : 10 ] Debug
[ ..\log\mainwindow.cpp : MainWindow : 11 ] Warning
[ ..\log\mainwindow.cpp : MainWindow : 12 ] Info

需要先恢复消息处理程序,才能看到输出,这样就ok了

但是这样去写debug太过麻烦,直接在头文件声明一个宏

#define cout qDebug() << "[" <<__FILE__ <<":"<<__FUNCTION__<<":"<<__LINE__ <<"]"

这样直接用cout << "";就可以了

回到正题

我们现在可以看到日志输出了,接下来就是将日志保存为一个.log格式的文件

具体写入什么信息,这个示情况而定,我这里是一股脑都写进去

先将函数改一下得到想要的信息

将信息封装一下

    QString str_type = QString("消息类型:%1\n").arg(localMsg.constData());
    QString str_file_line_function = QString("消息位置:%1:%2,%3\n").arg(file).arg(context.line).arg(function);
    QString str_date_time = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd") + '\n';
    QString message = str_type + str_file_line_function + str_date_time;

我们要写入.log的就是message

最后将函数修改如下:

void MainWindow::myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{

    QByteArray localMsg = msg.toLocal8Bit();
    const char *file = context.file ? context.file : "";
    const char *function = context.function ? context.function : "";
    QString str_type = QString("类型:%1\n").arg(localMsg.constData());
    QString str_file_line_function = QString("位置:%1:%2,%3\n").arg(file).arg(context.line).arg(function);
    QString str_date_time = "时间" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss ddd") + '\n';
    QString message = str_type + str_file_line_function + str_date_time;


    QString logPath = qApp->applicationDirPath() + "/log/"; /* log文件夹 */

    QDir dir(logPath);
    if(!dir.exists())
    {
        dir.mkpath(logPath);
    }
    /* log文件 */
    QString filePath = QString("%1%2.log").arg(logPath).arg(QDateTime::currentDateTime().toString("yyyy-MM-dd"));
    QFile logFile(filePath); /* 创建一个log文件 */
    /* 以只写方式打开,追加数据 */
    logFile.open(QIODevice::WriteOnly | QIODevice::Append);
    /* 文本数据流 */
    QTextStream text_stream(&logFile);
    /* 写入数据 */
    text_stream << message << "\r\n";
    logFile.flush();
    logFile.close();
}

这里我将枚举去掉了,如果想要写入某一种消息类型,再将switch加上作下判断即可,直接用if也ok

最后运行之后如下

 

这里以天为文本名,那么每天的日志就会另外保存为一个文件

 打开文件

类型:Debug
位置:..\log\mainwindow.cpp:10,MainWindow::MainWindow(QWidget*)
时间2020-01-15 14:25:15 周三

类型:Warning
位置:..\log\mainwindow.cpp:11,MainWindow::MainWindow(QWidget*)
时间2020-01-15 14:25:15 周三

类型:Info
位置:..\log\mainwindow.cpp:12,MainWindow::MainWindow(QWidget*)
时间2020-01-15 14:25:15 周三

 保存成功!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值