qInstallMessageHandler(重定向至log文件)

一、简单重定向

cpp 复制代码
#include <QApplication>
#include <QDebug>
#include <QFile>
#include <QTextStream>
#include <QLoggingCategory>
#include <iostream>
#include <QMutex>
#include <QDateTime>
    
    void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
    {
		//#1 create directory that name is log
		QDir dir("log");
		if (!dir.exists())
		{
			 QDir dir;
			 dir.mkdir("log");
		}
 
		//#2 create log file by current date ==> eg:log20170418.txt
		QString currentDate = QDateTime::currentDateTime().toString("yyyyMMdd");
		QString logName = "log" + currentDate + ".txt";
		QString logFileName = "log/" + logName;
 
		// 加锁
		static QMutex mutex;
		mutex.lock();
		QFile file(logFileName);
		if (!file.open(QIODevice::WriteOnly | QIODevice::Append))
		{
			file.close();
			return ;
		}
 
		//#3 joint string
		QString currentDateTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
		QString logMsg;
 
		switch (type)
		{
		case QtDebugMsg:
			logMsg = QString("%1: [Debug]: %2  Function: %3  File: %4  Line: %5\n").arg(currentDateTime).arg(msg).arg(context.function).arg(context.file).arg(context.line);
			break;
		case QtInfoMsg:
			logMsg = QString("%1: [Info]: %2  Function: %3  File: %4  Line: %5\n").arg(currentDateTime).arg(msg).arg(context.function).arg(context.file).arg(context.line);
			break;
		case QtWarningMsg:
			logMsg = QString("%1: [Warning]: %2 Function: %3 Line: %4 File: %5\n").arg(currentDateTime).arg(msg).arg(context.function).arg(context.file).arg(context.line);
			break;
		case QtCriticalMsg:
			logMsg = QString("%1: [Critical]: %2 Function: %3 Line: %4 File: %5\n").arg(currentDateTime).arg(msg).arg(context.function).arg(context.file).arg(context.line);
			break;
		case QtFatalMsg:
			logMsg = QString("%1: [Fatal]: %2 Function: %3 Line: %4 File: %5\n").arg(currentDateTime).arg(msg).arg(context.function).arg(context.file).arg(context.line);
			abort();
			break;
		default:
			break;
		}
 
		//#4 log message out to file
		QTextStream stream(&file);
		stream << logMsg << "\r\n";;
		file.flush();
		file.close();
		
		// 解锁
		mutex.unlock();
    }
 
    int main(int argc, char *argv[])
    {
        qInstallMessageHandler(customMessageHandler);
        QApplication a(argc, argv);
 
        qDebug()   << "this is Debug";
        qInfo()    << "this is info";
        qWarning() << "this is warning";
        qCritical()<< "this is critical";
        qFatal("this is fatal");
 
        return a.exec();
    }

二、

***.h

cpp 复制代码
#ifndef LOGGER_H
#define LOGGER_H

#include <QObject>
#include <QtMessageHandler>
#include <QDebug>
#include <QtDebug>
#include <QTextStream>
#include <QApplication>
#include <QDateTime>
#include <QMutex>
#include <QFile>
#include <QFileInfo>
#include <QMetaEnum>
#include <QSettings>
#include <QDir>
#define LOG_MAXSIZE  5 * 1024 //单个log文件最大值

class logger: public QObject
{
    Q_OBJECT
public:
    explicit logOutput(QObject *parent = nullptr);
    ~logOutput();
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    static void logThread::outPutMsg(QtMsgType type, const char *msg);
#else
    //信息处理函数(重写的myMessageHandler)
    /*功能说明:通过调试信息保存到日志文件
     *
     *参数说明:
     * msgType: 调试信息类型或级别(qdebug, qwarning, qfatal 。。。。)
     * context: 调试信息所处文本,可使用context.file和context.line获取文本所处行数及所处文件路径,以及使用context.function获取文本所处函数名
     * msg: 调试信息内容,自定义
    */
    static void outPutMsg(QtMsgType msgType, const QMessageLogContext &context, const QString &msg);
#endif

    static void install(); //安装信息处理函数
    static void uninstall(); //卸载信息处理函数
    static void deleteLog(); //删除过期日志

//    static void setLogMaxSize(qint64 maxsize); //设置单个日志文件最大值

private:
    /*
     * 函数功能:
     * 1、根据调试信息以及日期,保存到相应的文件。
     * 2、在保存文件前需要判断文件大小是否大于自定义值,如果大于,便按照序号从小到大新建一个。
     *
     *
    */
    static void saveLog(QString message, QString type);
    //参数为模块开关名
    static bool judgeSwitch(QString switchName); //判断模块是否存在
    static void write_ini_file(QString switchName); //写入ini信息
    static QVariant read_ini_file(QString switchName); //读取ini信息



};

#endif // LOGOUTPUT_H

***.cpp

cpp 复制代码
logger.cpp
#include "logger.h"
#include <QFile>

logger::logger(QObject *parent)
    : QObject{parent}
{

}

logger::~logOutput()
{
    qDebug("内存已释放");
}

//安装日志函数
void logger::install()
{
    //安装消息处理函数
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    qInstallMsgHandler(outPutMsg);
#else
    qInstallMessageHandler(outPutMsg);
#endif
    //创建log文件夹
    QString logPath = QApplication::applicationDirPath() + "/log";
    QDir dir(logPath);
        if(!dir.exists())
        {
            dir.mkdir(logPath);
            qDebug()<<"文件夹创建成功";
        }
    deleteLog(); //删除过期日志
}

//卸载日志函数
void logger::uninstall()
{
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
    qInstallMsgHandler(0);
#else
    qInstallMessageHandler(0);
#endif
}

//日志信息处理函数
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
void logger::outPutMsg(QtMsgType type, const char *msg)
#else
void logger::outPutMsg(QtMsgType msgType, const QMessageLogContext &context, const QString &msg)
#endif
{
    bool switchValue; //判断开关标志
    static QMutex mutex; //设置互斥锁
    //通过文件路径获取模块名比如widget.cpp
    QString switchKey = QString::fromLocal8Bit(context.file);
    int last = switchKey.lastIndexOf("\\");
    int leng = switchKey.length();
    switchKey = switchKey.right(leng-last-1);
    if(judgeSwitch(switchKey) == true) //判断对应模块配置属性是否存在
    {
        switchValue = read_ini_file(switchKey).toBool(); //读取配置的值
    }
    else
    {
        write_ini_file(switchKey); //新写入不存在的配置
        switchValue = read_ini_file(switchKey).toBool(); //读取配置的值
    }

    //判断信息类型
    QString type;
    switch (msgType) {
    case QtDebugMsg:
        type = QString("Debug");
        break;
    case QtWarningMsg:
        type = QString("Warning");
        switchValue = true; //警告信息不能通过开关关闭
        break;
    case QtCriticalMsg:
        type = QString("Critical");
        switchValue = true; //危险信息不能通过开关关闭
        break;
    case QtFatalMsg:
        type = QString("Fatal");
        switchValue = true; //致命信息不能通过开关关闭
        break;
    case QtInfoMsg:
        type = QString("Info");
    default:
        break;
    }

    if(switchValue == true) //判断配置开关是否打开
    {
        mutex.lock();  //互斥关锁
        //文件名和行数以及函数
        QString contextInfo = QString("[File:(%1), Line:(%2), Funtion(%3)]:").arg(context.file).arg(context.line).arg(context.function);
        //获取当前时间,精确到秒
        QString currentTime = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
        //拼接信息字符串
        QString message = QString("[%1] %2: %3 %4").arg(currentTime).arg(type).arg(contextInfo).arg(msg);
        //存入信息到日志文件
        saveLog(message, type);
        mutex.unlock(); //开锁
    }
}

//判断配置是否存在
bool logger::judgeSwitch(QString switchName)
{
    QSettings ini_config("logSetting.ini", QSettings::IniFormat);
    ini_config.beginGroup("class_Switch");
    bool isContains = ini_config.contains(switchName);
    ini_config.endGroup();
    return isContains;
}

//读取配置文件
QVariant logger::read_ini_file(QString switchName)
{
    QSettings ini_config("logSetting.ini", QSettings::IniFormat);
    ini_config.beginGroup("class_Switch");
    QVariant switchValue = ini_config.value(switchName);
    ini_config.endGroup();
    return switchValue;
}

//写入配置文件
void logger::write_ini_file(QString switchName)
{
    QSettings ini_config("logSetting.ini", QSettings::IniFormat);
    ini_config.beginGroup("class_Switch");
    ini_config.setValue(switchName, false);
    ini_config.endGroup();
}

//删除过期日志
void logger::deleteLog()
{
    //获取日志文件夹地址
    QString dirName =  QApplication::applicationDirPath() + "/log";
    QDir dir(dirName);
    //获取文件夹下所有文件信息列表
    QFileInfoList infoList = dir.entryInfoList(QDir::Files);
    //遍历日志文件
    foreach (QFileInfo fileInfo, infoList) {
        //将文件创建时间与过期时间作比较,如果创建时间小于过期时间,则删除(代码是一分钟期限,如果改天为单位可以使用adddays)
        if(fileInfo.birthTime() <= QDateTime::currentDateTime().addSecs(-1))
        {
            QFile::setPermissions(dirName + "/" +fileInfo.fileName(), QFileDevice::ReadOther | QFileDevice::WriteOther);
            if(QFile::remove(dirName + "/" +fileInfo.fileName()))
            {
                qDebug("日志删除成功!");
            }
            else
            {
                qDebug("日志删除失败!");
            }
        }

    }
}

//保存日志到文件
void logger::saveLog(QString message, QString type)
{
    int i = 1; //当文件大小超过最大值时,给新文件添加编号
    //以天为单位给文件命名
    QString fileName = QApplication::applicationDirPath() + "/log/" + QDateTime::currentDateTime().toString("yyyy-MM-dd")+ "_" + type + "_log";
    //文件名右边(后缀)
    QString fileNameRight;
    //最终要写入的文件名
    QString fileNameLast = fileName + ".txt";
    //绑定文件对象
    QFile file(fileNameLast);
    //判断文件大小
    while(file.size() >= LOG_MAXSIZE)
    {
        //给新文件加入序号后缀
        fileNameRight = QString("%1.txt").arg(i);
        //拼接最终文件名
        fileNameLast = fileName + fileNameRight;
        //修改file绑定的文件名
        file.setFileName(fileNameLast);
        i++;
    }
    //只写和拼接的方式打开文件
    bool isopen = file.open(QIODevice::WriteOnly | QIODevice::Append);
    if(isopen == true)
    {
        QTextStream write(&file);
        qDebug() << message;
        write << message << "\r\n";
        file.flush();
        file.close();
    }


}
cpp 复制代码
 int main(int argc, char *argv[])
    {
       logger::install();
        QApplication a(argc, argv);
 
        qDebug()   << "this is Debug";
        qInfo()    << "this is info";
        qWarning() << "this is warning";
        qCritical()<< "this is critical";
        qFatal("this is fatal");
 
        return a.exec();
    }

三、参数在配置文件设置(推荐)

****.h

cpp 复制代码
#ifndef LOGHANDLER_H
#define LOGHANDLER_H

#include <iostream>
#include <QDebug>
#include <QDateTime>
#include <QMutexLocker>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTimer>
#include <QTextStream>
#include <QTextCodec>

#include "configinfo.h"


struct LogHandlerPrivate {
    LogHandlerPrivate();
    ~LogHandlerPrivate();

    // 打开日志文件 log.txt,如果日志文件不是当天创建的,则使用创建日期把其重命名为 yyyy-MM-dd.log,并重新创建一个 log.txt
    void openAndBackupLogFile();
    void checkLogFiles(); // 检测当前日志文件大小
    void autoDeleteLog(); // 自动删除30天前的日志
    // 消息处理函数
    static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
    QDir logDir; // 日志文件夹
    QTimer renameLogFileTimer; // 重命名日志文件
    QTimer flushLogFileTimer; // 刷新输出到日志文件的定时器
    QDate logFileCreatedDate; // 日志文件创建的时间
    static QFile *logFile; // 日志文件
    static QTextStream *logOut; // 输出日志的 QTextStream,使用静态对象就是为了减少函数调用的开销
    static QMutex logMutex; // 同步使用的 mutex

    QString logPath;  // 日志的路径
    ConfigInfo m_ConfigInfo;

    static bool bDebugOnOff;
    int Days;
    int logLimitSize;

};


class LogHandler {
public:
    void installMessageHandler(); // 给Qt安装消息处理函数
    void uninstallMessageHandler(); // 取消安装消息处理函数并释放资源
    static LogHandler& Get() {
        static LogHandler m_logHandler;
        return m_logHandler;
    }
private:
    LogHandler();
    LogHandlerPrivate *d;
};

#endif // LOGHANDLER_H

****.cpp

cpp 复制代码
#include "loghandler.h"

// 初始化 static 变量
QMutex LogHandlerPrivate::logMutex;
QFile* LogHandlerPrivate::logFile = nullptr;
QTextStream* LogHandlerPrivate::logOut = nullptr;
bool LogHandlerPrivate::bDebugOnOff;

LogHandlerPrivate::LogHandlerPrivate()
{
    int renameTimer = m_ConfigInfo.find("LOG","RENAME_TIMER").toInt();
    int flushTimer = m_ConfigInfo.find("LOG","FLUSH_TIMER").toInt();
    bDebugOnOff = m_ConfigInfo.find("LOG","DEBUG").toString().contains("ON",Qt::CaseInsensitive);
    Days = m_ConfigInfo.find("LOG","DAYS").toInt();
    logLimitSize = m_ConfigInfo.find("LOG","LIMIT_SIZE").toInt();

    logDir.setPath("log"); // TODO: 日志文件夹的路径,为 exe 所在目录下的 log 文件夹,可从配置文件读取
    // 如果日志所在目录不存在,则创建
    if (!logDir.exists()) {
        logDir.mkpath("."); // 可以递归的创建文件夹
    }
    logPath = logDir.absoluteFilePath("today.log"); // 获取日志的路径

    // ========获取日志文件创建的时间========
    // 所以不能运行时使用这个函数检查创建时间,因为会在运行时变化,于是在程序启动时保存下日志文件的最后修改时间,
    logFileCreatedDate = QFileInfo(logPath).lastModified().date(); // 若日志文件不存在,返回nullptr

    // 打开日志文件,如果不是当天创建的,备份已有日志文件
    openAndBackupLogFile();

    // 十分钟检查一次日志文件创建时间
    renameLogFileTimer.setInterval(1000 * renameTimer); // TODO: 从配置文件读取
    renameLogFileTimer.start();

    QObject::connect(&renameLogFileTimer, &QTimer::timeout, [this] {
    QMutexLocker locker(&LogHandlerPrivate::logMutex);
    openAndBackupLogFile(); // 打开日志文件
    checkLogFiles(); // 检测当前日志文件大小
    autoDeleteLog(); // 自动删除30天前的日志
    });

    // 定时刷新日志输出到文件,尽快的能在日志文件里看到最新的日志
    flushLogFileTimer.setInterval(1000*flushTimer); // TODO: 从配置文件读取
    flushLogFileTimer.start();
    QObject::connect(&flushLogFileTimer, &QTimer::timeout, [] {
        // qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); // 测试不停的写入内容到日志文件
        QMutexLocker locker(&LogHandlerPrivate::logMutex);
        if (nullptr != logOut) {
            logOut->flush();
        }
    });
}
LogHandlerPrivate::~LogHandlerPrivate() {
    if (nullptr != logFile) {
        logFile->flush();
        logFile->close();
        delete logOut;
        delete logFile;
        // 因为他们是 static 变量


        logOut = nullptr;
        logFile = nullptr;
    }
}
// 打开日志文件 log.txt,如果不是当天创建的,则使用创建日期把其重命名为 yyyy-MM-dd.log,并重新创建一个 log.txt
void LogHandlerPrivate::openAndBackupLogFile() {
    // 总体逻辑:
    // 1. 程序启动时 logFile 为 nullptr,初始化 logFile,有可能是同一天打开已经存在的 logFile,所以使用 Append 模式
    // 2. logFileCreatedDate is nullptr, 说明日志文件在程序开始时不存在,所以记录下创建时间
    // 3. 程序运行时检查如果 logFile 的创建日期和当前日期不相等,则使用它的创建日期重命名,然后再生成一个新的 log.txt 文件
    // 4. 检查日志文件超过 LOGLIMIT_NUM 个,删除最早的
    // 备注:log.txt 始终为当天的日志文件,当第二天,会执行第3步,将使用 log.txt 的创建日期重命名它

   // QString logPath = logDir.absoluteFilePath("today.log"); // log.txt的路径
    // [[1]] 程序每次启动时 logFile 为 nullptr
    if (logFile == nullptr) {
        logFile = new QFile(logPath);
        logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) ? new QTextStream(logFile) : nullptr;
        if (logOut != nullptr)
            logOut->setCodec("UTF-8");
        // [[2]] 如果文件是第一次创建,则创建日期是无效的,把其设置为当前日期
        if (logFileCreatedDate.isNull()) {
            logFileCreatedDate = QDate::currentDate();
        }
    }
    // [[3]] 程序运行时如果创建日期不是当前日期,则使用创建日期重命名,并生成一个新的 log.txt
    if (logFileCreatedDate != QDate::currentDate()) {
        logFile->flush();
        logFile->close();
        delete logOut;
        delete logFile;
        QString newLogPath = logDir.absoluteFilePath(logFileCreatedDate.toString("yyyy-MM-dd.log"));;
        QFile::copy(logPath, newLogPath); // Bug: 按理说 rename 会更合适,但是 rename 时最后一个文件总是显示不出来,需要 killall Finder 后才出现
        QFile::remove(logPath); // 删除重新创建,改变创建时间
        // 重新创建 log.txt
        logFile = new QFile(logPath);
        logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) : nullptr;
        logFileCreatedDate = QDate::currentDate();
        if (logOut != nullptr)
        logOut->setCodec("UTF-8");
    }
}
// 检测当前日志文件大小
void LogHandlerPrivate::checkLogFiles() {
    // 如果 protocal.log 文件大小超过5M,重新创建一个日志文件,原文件存档为yyyy-MM-dd_hhmmss.log
    if (logFile->size() > 1024*logLimitSize)
    {
        logFile->flush();
        logFile->close();
        delete logOut;
        delete logFile;
        //QString logPath = logDir.absoluteFilePath("today.log"); // 日志的路径
        QString newLogPath = logDir.absoluteFilePath(logFileCreatedDate.toString("yyyy-MM-dd.log"));
        QFile::copy(logPath, newLogPath); // Bug: 按理说 rename 会更合适,但是 rename 时最后一个文件总是显示不出来,需要 killall Finder 后才出现
        QFile::remove(logPath); // 删除重新创建,改变创建时间
        logFile = new QFile(logPath);
        logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) : NULL;
        logFileCreatedDate = QDate::currentDate();
        if (logOut != nullptr)
        logOut->setCodec("UTF-8");
    }
}
// 自动删除30天前的日志
void LogHandlerPrivate::autoDeleteLog()
{
    QDateTime now = QDateTime::currentDateTime();
    // 前30天
    QDateTime dateTime1 = now.addDays(-Days);
    QDateTime dateTime2;
    //QString logPath = logDir.absoluteFilePath("today.log"); // 日志的路径
    QDir dir(logPath);
    QFileInfoList fileList = dir.entryInfoList();
    foreach (QFileInfo f, fileList ) {
        // "."和".."跳过
        if (f.baseName() == "")
        continue;
        dateTime2 = QDateTime::fromString(f.baseName(), "yyyy-MM-dd");
        if (dateTime2 < dateTime1) { // 只要日志时间小于前30天的时间就删除
            dir.remove(f.absoluteFilePath());
        }
    }
}

// 消息处理函数
void LogHandlerPrivate::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
    QMutexLocker locker(&LogHandlerPrivate::logMutex);
    bool switchValue =true; //判断开关标志
    QString level;
    switch (type) {
        case QtDebugMsg:
        level = "DEBUG";
        switchValue = LogHandlerPrivate::bDebugOnOff;
        break;
        case QtInfoMsg:
        level = "INFO ";
        break;
        case QtWarningMsg:
        level = "WARN ";
        break;
        case QtCriticalMsg:
        level = "ERROR";
        break;
        case QtFatalMsg:
        level = "FATAL";
        break;
        default:
        break;
    }
// 输出到标准输出: Windows 下 std::cout 使用 GB2312,而 msg 使用 UTF-8,但是程序的 Local 也还是使用 UTF-8
#if defined(Q_OS_WIN)
    QByteArray localMsg = QTextCodec::codecForName("GB2312")->fromUnicode(msg); //msg.toLocal8Bit();
#else
    QByteArray localMsg = msg.toLocal8Bit();
#endif
    if (nullptr == LogHandlerPrivate::logOut)
        return;


    // 输出到日志文件
/*     QString fileName = context.file;
    int index = fileName.lastIndexOf(QDir::separator());
    fileName = fileName.mid(index + 1);
    // 格式: 时间 - [Level] (文件名:行数, 函数): 消息
    QString message = QString("[%1] - [%2] (%3:%4, %5): %6\n")
    .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(level)
    .arg(fileName).arg(context.line).arg(context.function).arg(msg);*/

    //  格式: 时间 - [Level] : 消息
    QString message = QString("[%1]-[%2] : %3\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(level).arg(msg);

    if (switchValue) {
        std::cout << std::string(localMsg) << std::endl; //控制台输出
        (*LogHandlerPrivate::logOut) << message;
    }
}
/************************************************************************************************************
* *
* LogHandler *
* *
***********************************************************************************************************/
LogHandler::LogHandler() : d(nullptr) {

}



// 给Qt安装消息处理函数
void LogHandler::installMessageHandler() {
    QMutexLocker locker(&LogHandlerPrivate::logMutex); // 类似C++11的lock_guard,析构时自动解锁
    if (nullptr == d) {
        d = new LogHandlerPrivate();
        qInstallMessageHandler(LogHandlerPrivate::messageHandler); // 给 Qt 安装自定义消息处理函数
    }
}
// 取消安装消息处理函数并释放资源
void LogHandler::uninstallMessageHandler() {
    QMutexLocker locker(&LogHandlerPrivate::logMutex);
    qInstallMessageHandler(nullptr);
    delete d;
    d = nullptr;
}

****.h

cpp 复制代码
#ifndef CONFIGINFO_H
#define CONFIGINFO_H

#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QSettings>

class ConfigInfo
{
public:
    ConfigInfo();
    ~ConfigInfo();


    QVariant find(const QString strGroup, const QString value);

private:
    QSettings *m_config;
};

#endif // CONFIGINFO_H

****.cpp

cpp 复制代码
#include "configinfo.h"

ConfigInfo::ConfigInfo()
{
    QDir ConfigDir;
    ConfigDir.setPath("config");
    if (!ConfigDir.exists()) {
        ConfigDir.mkpath("."); // 可以递归的创建文件夹
    }

    QString ConfigPath = ConfigDir.absoluteFilePath("config.ini"); // 获取路径

    m_config = new QSettings(ConfigPath, QSettings::IniFormat);

}

ConfigInfo::~ConfigInfo()
{
    if(m_config)
        delete m_config;
}

QVariant ConfigInfo::find(const QString strGroup, const QString value)
{
    m_config->beginGroup(strGroup);
    QVariant switchValue = m_config->value(value);
    m_config->endGroup();
    return switchValue;
}

*****.ini

cpp 复制代码
[LOG]
RENAME_TIMER = 2
FLUSH_TIMER = 1
DEBUG = on

# 自动删除30天前的日志
DAYS = 30

#文件大小5M
LIMIT_SIZE = 5

main.cpp

cpp 复制代码
#include "mainwindow.h"
#include "loghandler.h"


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

    // [[1]] 安装消息处理函数
    LogHandler::Get().installMessageHandler();

    MainWindow w;
    w.show();

    // [[2]] 输出测试,查看是否写入到文件
    qDebug() << "Hello";
    qInfo() << QString("God bless you!");

/*
    // [[3]] 取消安装自定义消息处理,然后启用
    LogHandler::Get().uninstallMessageHandler();
    qDebug() << "........"; // 不写入日志
    LogHandler::Get().installMessageHandler();
 */
    int ret = app.exec(); // 事件循环结束
    // [[4]] 程序结束时释放 LogHandler 的资源,例如刷新并关闭日志文件
    LogHandler::Get().uninstallMessageHandler();
    return ret;
}

https://doc.qt.io/qt-6/zh/qtlogging.html

https://blog.csdn.net/weixin_42661333/article/details/129127694

https://2048ai.net/6825b5dba5baf817cf4c6331.html?spm=1001.2101.3001.6650.18&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Eactivity-18-130181874-blog-129127694.235%5Ev43%5Epc_blog_bottom_relevance_base1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Eactivity-18-130181874-blog-129127694.235%5Ev43%5Epc_blog_bottom_relevance_base1

相关推荐
yddddddy2 小时前
关于vue3
开发语言·vue
豆沙沙包?2 小时前
2025年--Lc231-350. 两个数组的交集 II-Java版
java·开发语言
whm27772 小时前
Visual Basic创建工具栏
开发语言·visual studio
程序猿20232 小时前
Python每日一练---第九天:H指数
开发语言·python
是烟花哈2 小时前
后端开发CRUD实现
java·开发语言·spring boot·mybatis
海盗猫鸥3 小时前
「C++」vector的使用及接口模拟详解
开发语言·c++
wjs20243 小时前
CSS 下拉菜单:设计与实践指南
开发语言
天道有情战天下3 小时前
Lua使用
开发语言·lua
曲鸟3 小时前
用Python和MediaPipe实现实时手指识别
开发语言·python