Qt - 写日志文件

1. 概述

Qt中两种实现记录日志的方式

第一种是安装自定义的Qt消息处理程序,自动输出程序产生的调试消息、警告、关键和致命错误消息的函数。

第二种是自定义一个类,可以在程序指定位置打印输出指定的内容。

 

1.1 第一种:qInstallMessageHandler方式

自定义消息处理函数,然后安装该函数,注意此时QDebug的消息将会输出在日志文件,Qt程序调式时不再打印。

 

在main.cpp文件中添加以下头文件和代码:

#include "mainwindow.h"
​
#include <QApplication>
#include <QDateTime>
#include <QMutex>
#include <QFile>
#include <QTextStream>
#include <QDebug>
​
//#define LOG_FILE     qApp->applicationDirPath()+"/logger.txt"
​
void outputMessage(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    static QMutex mutex;
    mutex.lock();
​
    QString text;
    switch(type)
    {
    case QtDebugMsg:
       text = QString("Debug:");
       break;
​
    case QtWarningMsg:
       text = QString("Warning:");
       break;
​
    case QtCriticalMsg:
       text = QString("Critical:");
       break;
​
    case QtFatalMsg:
       text = QString("Fatal:");
    default:
        break;
    }
​
    QString context_info = QString("[%1 %2]:").arg(QString(context.function)).arg(context.line);//文件名,所在行数
    QString current_date = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    QString message = QString("%1 %2 %3").arg(current_date).arg(text).arg(msg);//.arg(context_info).arg(msg);
​
    QFile file("log.txt");
   file.open(QIODevice::WriteOnly | QIODevice::Append);
    QTextStream text_stream(&file);
    text_stream << message << "\r\n";
    file.flush();
    file.close();
​
    mutex.unlock();
}
int main(int argc, char *argv[])
{
    qInstallMessageHandler(outputMessage);//安装消息处理器调用回调函数
    
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}
​
​

 

在mainwindow.cpp源文件中添加以下代码

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
​
#define cout qDebug()<<"["<<__FUNCTION__<<":"<<__LINE__<<"]"
#pragma execution_character_set("utf-8")
​
MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    qDebug() << u8"这是一个写日志的测试";          // 调试信息提示
    qWarning() << u8"这是一个写日志的测试";        // 一般的警告提示
    qCritical() << u8"这是一个写日志的测试";       // 严重的错误提示
    qFatal("qFatal %s", "这是一个写日志的测试");   // 致命错误不能用<<输出;
}
​
MainWindow::~MainWindow()
{
    delete ui;
}
​
​

 

日志文件在项目里能看到:

img

 

日志内容:

 

 

 

1.2 第二种:自定义一个类,实现输出日志接口

定义一个日志类,实现输出日志到指定文件的接口,在需要输出日志的地方调用接口即可。

clog.h

#ifndef CLOG_H
#define CLOG_H

#include <QFile>

class  CLog : public QObject
{
    Q_OBJECT
public:

    /*!
     *  @brief 日志等级
     */
    enum CLOG_LEVEL
    {
        RINFO,               /*!<    提示  */
        RWARNING,            /*!<    警告  */
        RERROR               /*!<    错误  */
    };
    Q_FLAG(CLOG_LEVEL)

    struct LogConfig
    {
        bool isRecord2File;     /*!< 是否记录到文件 */
        int level;              /*!< 记录日志等级,大于等于此level的日志将被记录 */
    };

    CLog();

    void setLogLevel(const CLog::CLOG_LEVEL & level);
    CLog::CLOG_LEVEL getLogLevel(){return logLevel;}

    static bool init(CLog::LogConfig & logConfig);
    static bool createDir(QString dirPath);

    static void log(CLOG_LEVEL nLevel,const char * fileDesc,const char * functionDesc, int lineNum,const char* data, ...);

private:
    static QString getLeveDesc(CLOG_LEVEL level);

private:
    static bool isRecord2File;
    static bool isFileReady;
    static CLOG_LEVEL logLevel;
};

#ifdef Q_OS_WIN
#define FILE_SEPARATOR '\\'
#else
#define FILE_SEPARATOR '/'
#endif

#define FUNC_SEPARATOR '::'

#ifndef QT_NO_DEBUG
#define __FILENAME__ (strrchr(__FILE__, FILE_SEPARATOR) ? (strrchr(__FILE__, FILE_SEPARATOR) + 1):__FILE__)
#define __FUNNAME__ (strrchr(__FUNCTION__,FUNC_SEPARATOR)?(strrchr(__FUNCTION__, FUNC_SEPARATOR) + 1):__FUNCTION__)
#else
#define __FILENAME__ NULL
#define __FUNNAME__ NULL
#endif

#define CLOG_INFO(...)       CLog::log(CLog::RINFO,__FILENAME__,__FUNNAME__,__LINE__, __VA_ARGS__)
#define CLOG_WARNING(...)    CLog::log(CLog::RWARNING, __FILENAME__,__FUNNAME__, __LINE__,__VA_ARGS__)
#define CLOG_ERROR(...)      CLog::log(CLog::RERROR,__FILENAME__,__FUNNAME__,__LINE__,__VA_ARGS__)

#endif // CLOG_H

 

clog.cpp

#include "clog.h"

#include <QApplication>
#include <QDir>
#include <QDate>
#include <QMetaEnum>
#include <QThread>
#include <QMutex>
#include <QMutexLocker>
#include <stdarg.h>

#include <QDebug>

const char PATH_LogPath[] = "/../logs";
const char Suffix[] = ".log";

#define MAX_LOG_LENGH 1024

bool CLog::isFileReady = false;
bool CLog::isRecord2File = true;
CLog::CLOG_LEVEL CLog::logLevel = CLog::RINFO;           //默认是info级

QFile localFile;
QMutex mutex;

CLog::CLog()
{
}

void CLog::setLogLevel(const CLog::CLOG_LEVEL &level)
{
    logLevel = level;
}

bool CLog::init(LogConfig &logConfig)
{
    isRecord2File = logConfig.isRecord2File;
    logLevel = (CLog::CLOG_LEVEL)logConfig.level;

    QString logDir = qApp->applicationDirPath() +  QString(PATH_LogPath);
    if(createDir(logDir))
    {
        QString fileName = logDir + QDir::separator() + QDate::currentDate().toString("yyyy-MM-dd") + QString(Suffix);
        localFile.setFileName(fileName);
        if(localFile.open(QFile::WriteOnly|QFile::Append|QFile::Text))
        {
            isFileReady = true;
        }
    }
    return isFileReady;
}

bool CLog::createDir(QString dirPath)
{
    QFileInfo fileInfo(dirPath);
    if(!fileInfo.exists())
    {
        QDir tmpDir;
        return tmpDir.mkpath(dirPath);
    }

    return true;
}

void CLog::log(CLOG_LEVEL nLevel, const char *fileDesc, const char *functionDesc, int lineNum, const char* data, ...)
{
    QMutexLocker locker(&mutex);
    if(isFileReady && nLevel >= logLevel)
    {
        QString recordInfo = QString("[%1]").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz"));
        recordInfo.append(getLeveDesc(nLevel));

#ifndef QT_NO_DEBUG
        recordInfo.append(QString("[%1:%2:%3]").arg(fileDesc).arg(functionDesc).arg(lineNum));
#endif
        va_list vlist;
        va_start(vlist,data);

        QByteArray byteArray;
#if defined(Q_OS_WIN)
        int recordLen = _vscprintf(data,vlist);
        byteArray.resize(recordLen);
#else
        byteArray.resize(1024);
#endif
        vsprintf(byteArray.data(),data,vlist);
        recordInfo.append(byteArray);
        va_end(vlist);

        recordInfo.append("\n");

        if(isRecord2File){
            localFile.write(recordInfo.toLocal8Bit().data(),recordInfo.toLocal8Bit().length());
            localFile.flush();
        }else{
//            qDebug()<<recordInfo;
        }
    }
}

QString CLog::getLeveDesc(CLog::CLOG_LEVEL level)
{
#if (QT_VERSION > 0x050500)
    static QMetaEnum metaEnum = QMetaEnum::fromType<CLog::CLOG_LEVEL>();
    return QString("[%1]").arg(metaEnum.key(level));
#else
    switch(level)
    {
        case CLOG_LEVEL::INFO:
                                return "[INFO]";
        case CLOG_LEVEL::INFO:
                                return "[WARNING]";
        case CLOG_LEVEL::INFO:
                                return "[ERROR]";
    }
#endif
}

 

简单调用示例:

CLog::LogConfig logConfig;
logConfig.isRecord2File = true;
logConfig.level = 0;
CLog::init(logConfig);
CLOG_INFO("df2008");

 

输出日志:
[2020-12-03 13:15:14:549][RINFO][widget.cpp:Widget:17]df2008

 

posted @ 2022-07-16 20:08  [BORUTO]  阅读(1807)  评论(0)    收藏  举报