(Qt) 重定向内置日志

前言

在软件开发过程中,避免不了日志的使用。

在Qt中,我们平常用的#include <QDebug>就是Qt自带的日志的一部分,平常我们最常用的就是在控制台进行输出。

而本文通过自定义重定向qInstallMessageHandler(),来将信息打到日志文件中。

代码

E:\Qt\demo\saved\LOG>tree /f
卷 新加卷 的文件夹 PATH 列表
卷序列号为 BAEE-AEDC
E:.
│  log_test.pro
│  main.cpp
│
└─log
        LOG.cpp
        LOG.h
        log.pri
        LOG_Config.hpp

.pri 独立的包

log.pri

QT += widgets

INCLUDEPATH += $$PWD

CONFIG(release, debug|release) {
    
    
    # 取消release优化
    # 取消优化这些信息:文件名、函数名、行数
    DEFINES += QT_MESSAGELOGCONTEXT
    # 消除debug输出
    DEFINES += QT_NO_DEBUG_OUTPUT
}

HEADERS += \
    $$PWD/LOG.h \
    $$PWD/LOG_Config.hpp


SOURCES += \
    $$PWD/LOG.cpp

LOG_Config.hpp

#ifndef __LOG__CONFIG__HPP__BY__CuberLotus__
#define __LOG__CONFIG__HPP__BY__CuberLotus__

namespace LOG {
    
    

/// 保存路径
const char * const _SAVE_PATH_ = "./myFiles/log";
/// 文件后缀名
const char * const _FILE_SUFFIX_NAME_ = ".log";

/// 根据日期分文件夹
/// 这里是具体日志的内容
const char * const _DATA_FIRMAT_ = "yyyy年MM月dd日";
const char * const _TIME_FORMAT_ = "hh:mm:ss";

} // namespace LOG

#endif // __LOG__CONFIG__HPP__BY__CuberLotus__

LOG.h

#ifndef __LOG__H__BY__CuberLotus__
#define __LOG__H__BY__CuberLotus__
#include <QDebug>

namespace LOG {
    
    
/// 给外部调用设置的唯一接口
void QLog_Init();
}

#endif  // __LOG__HPP__BY__CuberLotus__

LOG.cpp

#include <QApplication>
#include <QDateTime>
#include <QDebug>
#include <QDir>

#include "LOG.h"
#include "LOG_Config.hpp"

namespace LOG {
    
    
/// data
/// 软件版本
static QString appVersion;
/// 日志存储路径
static QString logPath;
/// 日志文件后缀名
static QString suffixName;
/// 日期时间格式
static QString timeFormat;
/// 日志类型的字符串
static QVector<QString> typeStrList;

/**
 * @brief init_Config
 * 初始化配置
 * enum QtMsgType {
 *  QtDebugMsg,
 *  QtWarningMsg,
 *  QtCriticalMsg,
 *  QtFatalMsg,
 *  QtInfoMsg,
 *  QtSystemMsg = QtCriticalMsg
 * };
 */
static void init_config() {
    
    
    /// V1.2.3.4
    appVersion = QCoreApplication::applicationVersion();
    if (appVersion.isEmpty()) {
    
    
        appVersion = "No Version";
    } else {
    
    
        appVersion = "V" + appVersion;
    }

    /// .log
    suffixName = QString(_FILE_SUFFIX_NAME_);
    /// hh:mm:ss
    timeFormat = QString(_TIME_FORMAT_);
    /// ./myFiles/log/yyyy年MM月dd日
    logPath = QDir(_SAVE_PATH_).absoluteFilePath(
                QDateTime::currentDateTime().toString(_DATA_FIRMAT_));
    /// 生成path
    QDir().mkpath(logPath);

    typeStrList << QString("Debug") 
        		<< QString("Warning") 
        		<< QString("Critical")
             	<< QString("Fatal") 
        		<< QString("Info") 
        		<< QString("System");
}

/**
 * @brief QtMessageHandler
 * @param type
 * @param context
 * @param msg
 * 重定向的回调函数
 * 样例:[V0.1.2.0] [18:08:52] [@File:..\LOG\main.cpp @Func:int qMain(int,
 * char**) @Line:16]
 */
static void QtMessageHandler(QtMsgType type, const QMessageLogContext& context,
                      const QString& msg) {
    
    
    QString filePath = QDir(logPath).absoluteFilePath(
                typeStrList[type] + suffixName);
    QString&& time = QDateTime::currentDateTime().toString(timeFormat);
    QString&& locate = QString("@File:%1 @Func:%2 @Line:%3")
                           .arg(context.file)
                           .arg(context.function)
                           .arg(context.line);
    /// 注意这里使用 \r\n
    QString&& str = QString("[%1] [%2] [%3]\r\n%4\r\n")
                        .arg(appVersion)
                        .arg(time)
                        .arg(locate)
                        .arg(msg);

    QFile file(filePath);
    file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream(&file) << str;
    file.close();
}

/// 给外部调用设置的唯一接口
void QLog_Init() {
    
    
    init_config();
#ifdef QT_NO_DEBUG
    qInstallMessageHandler(QtMessageHandler);
#endif
}

}  // namespace LOG

example

log_test.pro

QT += widgets

DESTDIR = $$PWD/bin/

VERSION = 0.1.3
TARGET = log_test_V$$VERSION

CONFIG(debug, debug|release) {
    
    
    CONFIG += console
}

INCLUDEPATH += code
include($$PWD/log/log.pri)

SOURCES += \
    main.cpp

main.cpp

#include <QApplication>
#include <QDebug>
#include "LOG.h"

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

    LOG::QLog_Init();

    qDebug("This is a debug message");
    qWarning("This is a warning message");
    qCritical("This is a critical message");
    qInfo() << "这是" << "链式操作" << __func__ << __FUNCTION__ << __PRETTY_FUNCTION__;

    return 0;
    // return __app.exec();
}

使用效果

debug模式

debug模式,设定为在控制台输出

在这里插入图片描述

release模式

release模式设定为记录到文件中

E:\Qt\demo\saved\LOG\bin>tree /f
卷 新加卷 的文件夹 PATH 列表
卷序列号为 BAEE-AEDC
E:.
│  log_test_V0.1.3.exe
│
└─myFiles
    └─log
        └─2023年04月18日
                Critical.log
                Info.log
                Warning.log

在这里插入图片描述

分析

Qt内部结构

/// qlogging.h
enum QtMsgType {
    
     
    QtDebugMsg, 
    QtWarningMsg, 
    QtCriticalMsg, 
    QtFatalMsg, 
    QtInfoMsg, 
    QtSystemMsg = QtCriticalMsg 
};

class Q_CORE_EXPORT QMessageLogger {
    
    
#ifndef Q_CC_MSVC
    Q_NORETURN
#endif
    Q_DECL_COLD_FUNCTION
    /// 最高危险等级,使用一次程序直接终止
    void fatal(const char *msg, ...) const noexcept Q_ATTRIBUTE_FORMAT_PRINTF(2, 3);

#ifndef QT_NO_DEBUG_STREAM
    QDebug debug() const;
    QDebug debug(const QLoggingCategory &cat) const;
    QDebug debug(CategoryFunction catFunc) const;
    QDebug info() const;
    QDebug info(const QLoggingCategory &cat) const;
    QDebug info(CategoryFunction catFunc) const;
    QDebug warning() const;
    QDebug warning(const QLoggingCategory &cat) const;
    QDebug warning(CategoryFunction catFunc) const;
    QDebug critical() const;
    QDebug critical(const QLoggingCategory &cat) const;
    QDebug critical(CategoryFunction catFunc) const;

    QNoDebug noDebug() const noexcept;
#endif // QT_NO_DEBUG_STREAM
};

#define qDebug QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).debug
#define qInfo QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).info
#define qWarning QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).warning
#define qCritical QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).critical
#define qFatal QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC).fatal

/// 重定向的设定
typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);

核心函数

#include <QApplication>

/**
 * @brief QtMessageHandler
 * @param type     日志类型
 * @param context  日志上下文
 * @param msg      日志的信息
 * 重定向的回调函数
 */
void QtMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) {
    
    
}

int main(int argc, char *argv[]) {
    
    
    /// 必须有 QApplication 的实例
    QApplication a(argc, argv);
    /// 全局函数
    qInstallMessageHandler(QtMessageHandler);
}

核心配置

# QApplication 所在的库
QT += widgets

# 由于release模式会对程序进行优化
# 通过设定 DEFINES 取消这些优化
# 并取消 debug 信息的输出
CONFIG(release, debug|release) {
    
    
    # 取消release优化
    # 取消优化这些信息:文件名、函数名、行数
    DEFINES += QT_MESSAGELOGCONTEXT
    # 消除debug输出
    DEFINES += QT_NO_DEBUG_OUTPUT
}



END

猜你喜欢

转载自blog.csdn.net/CUBE_lotus/article/details/130233809