Qt 日志规范及原理分析

Qt中debug和release状态下输出内容有所不同,debug模式下包含了数百条警告及错误日志,除非在编译期间设置了QT_NO_WARNING_OUTPUT和/或QT_NO_DEBUG_OUTPUT,否则在发布模式下构建的Qt也包含此类警告。

一 格式化日志

qt 格式化输出内容有两种方法: qInstallMessageHandler 和 qSetMessagePattern

qSetMessagePattern 使用方法

Placeholder

Description

%{appname}

QCoreApplication::applicationName()

%{category}

Logging category

%{file}

Path to source file

%{function}

Function

%{line}

Line in source file

%{message}

The actual message

%{pid}

QCoreApplication::applicationPid()

%{threadid}

The system-wide ID of current thread (if it can be obtained)

%{qthreadptr}

A pointer to the current QThread (result of QThread::currentThread())

%{type}

"debug", "warning", "critical" or "fatal"

%{time process}

time of the message, in seconds since the process started (the token "process" is literal)

%{time boot}

the time of the message, in seconds since the system boot if that can be determined (the token "boot" is literal). If the time since boot could not be obtained, the output is indeterminate (see QElapsedTimer::msecsSinceReference()).

%{backtrace [depth=N] [separator="..."]}

A backtrace with the number of frames specified by the optional depth parameter (defaults to 5), and separated by the optional separator parameter (defaults to "|"). This expansion is available only on some platforms (currently only platfoms using glibc). Names are only known for exported functions. If you want to see the name of every function in your application, use QMAKE_LFLAGS += -rdynamic. When reading backtraces, take into account that frames might be missing due to inlining or tail call optimization.

QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"

#define QT_MESSAGE_PATTERN="[%{time yyyyMMdd h:mm:ss.zzz t} %{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{file}:%{line} - %{message}"


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

    QString str1 = QString("%1/%2/%3/%4").arg(1).arg(2).arg(3).arg(4);
    qDebug() << str1;

    return a.exec();

}

输出结果: Debug:[20190611 21:12:15.517 中国标准时间Deww] ..\testListCrash\main.cpp:96 - "1/2/3/4"

Release下: [20190611 21:10:32.516 中国标准时间Deww] unknown:0 - "1/2/3/4"

输出结果不同,因为release模式下 系统默认使用了 QT_NO_DEBUG_OUTPUT的宏定义,导致输出时获取不到 file  function  line的内容。 解决: qt工程 在.pro文件中添加 DEFINES += QT_MESSAGELOGCONTEXT , vs工程中 属性--C++预处理器 ,预处理器中删除 QT_NO_DEBUG_OUTPUT,添加 QT_MESSAGELOGCONTEXT 。工程修改好后重新输出,release下也能听获取详细信息。

日志输出到指定文件中:

在main中添加 qInstallMessageHandler(myMessageOutput) 方法捕获消息内容, myMessageOutput 中使用 qFormatLogMessage 获取消息内容

qInstallMessageHandler  qt中定义的日志消息捕获器,能够重新格式化输出日志内容,使用方法:

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

int main(int argc, char *argv[])
{
      qInstallMessageHandler(myMessageOutput);
      QApplication app(argc, argv);
      
      qDebug() << "hello";
      return app.exec();
}

输出 Debug模式下 hello, line- 79, release 模式下 hello, line- 0 ,获取行数失败,原因同上,需要在 pro 或者vs工程文件中添加 QT_MESSAGELOGCONTEXT 的宏定义。添加完成后,输出正常。

二 日志内容输出到文件中

上面的日志格式设置好后,在运行qt工程时可以输出到控制台中,需要将 myMessageOutput 中的内容缓存写到文件中。

QFile *m_file;
m_file = new QFile(1.txt“”);

if (!m_file->open(QIODevice::WriteOnly)) {
        delete m_file;
        m_file = NULL;
        return;
    }

m_file->write(stderr, size);
m_file->flush();

 

三 日志源码分析

  • DEBUG qDebug该级别日志的主要作用是对系统每一步的运行状态进行精确的记录。可以将各类详细信息记录到DEBUG里,起到调试的作用,包括参数信息、调试细节信息、返回值信息等。
  • INFO   qInfo该种日志记录系统的正常运行状态,通过查看INFO级别的日志,可以很快地对系统中出现的 WARN,ERROR,FATAL错误进行定位。可以将初始化系统配置、业务状态变化信息,或者用户业务流程中的核心处理记录INFO日志中,方便日常运维工作以及错误回溯时上下文场景复现。
  • WARN  qWarning该日志表示系统可能出现问题
  • ERROR qCritical该级别的错误也需要马上被处理,但是紧急程度要低于FATAL级别。ERROR应该尽量详细记录。
  • FATAL  qFatal需要立即被处理的系统级错误。系统需要将错误相关痕迹以及错误细节记录FATAL日志中,方便后续人工回溯解决。

qt中提供5种级别日志,qDebug   qInfo   qWarning  qCritical   qFatal 是并列的日志级别, 以下只以 qDebug的源码进行分析,其他的类似

定义: 

#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug
qDebug() << "hello";

qDebug("hello  %s", str);

 以上是qDebug的两种调用方式, 传可变参数和使用 << 

传可变参数时,可变参数的原理及介绍参考 https://blog.csdn.net/youwuwei2012/article/details/28239089 文档。

void QMessageLogger::debug(const char *msg, ...) const
{
    va_list ap;
    va_start(ap, msg); // use variable arg list
    const QString message = qt_message(QtDebugMsg, context, msg, ap); //根据不同的消息级别
                                                                      // 分 , 如qWarning 传    
                                                               //QtWarningMsg
    va_end(ap);

    if (isFatal(QtDebugMsg))
        qt_message_fatal(QtDebugMsg, context, message);
}

static QString qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap)
{
    QString buf = QString::vasprintf(msg, ap); //分割参数,输出 QString 格式
    qt_message_print(msgType, context, buf);
    return buf;
}

static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
{
...
(*messageHandler.load())(msgType, context, message); //前面定义的消息捕获回调函数        
                                              //qInstallMessageHandler(QtMessageOutput)
...
}
qDebug() << 的使用原理如下
// 构造 QDebug 类的对象,拷贝内容,返回 QDebug 对象
QDebug QMessageLogger::debug() const
{
    QDebug dbg = QDebug(QtDebugMsg);
    QMessageLogContext &ctxt = dbg.stream->context;
    ctxt.copy(context);
    return dbg;
}

//追加内容
inline QDebug &operator<<(signed int t) { stream->ts << t; return maybeSpace(); }

那么 什么时候将内容输出呢?析构函数中输出了
QDebug::~QDebug()
{
    if (!--stream->ref) {
        if (stream->space && stream->buffer.endsWith(QLatin1Char(' ')))
            stream->buffer.chop(1);
        if (stream->message_output) {
            qt_message_output(stream->type,
                              stream->context,
                              stream->buffer);
        }
        delete stream;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值