(Qt) Redirect built-in logs

foreword

In the software development process, the use of logs cannot be avoided.

In Qt, what we usually use #include <QDebug>is part of the log that comes with Qt, and the most common thing we usually use is to output on the console.

However, this article uses custom redirection qInstallMessageHandler()to type information into the log file.

the code

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 Standalone package

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();
}

Effect

debug mode

debug mode, set to output in the console

insert image description here

release mode

The release mode is set to record to a file

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

insert image description here

analyze

Qt internal structure

/// 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);

core function

#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);
}

core configuration

# QApplication 所在的库
QT += widgets

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



END

Guess you like

Origin blog.csdn.net/CUBE_lotus/article/details/130233809