Qt 日志文件系统
- 一、注册日志消息处理程序
- 二、日志类型
-
- QtDebugMsg(调试信息)
- QtWarningMsg(警告信息)
- QtCriticalMsg、QtSystemMsg(关键消息、系统信息)
- QtFatalMsg(错误信息)
- 三、自定义的日志架构
-
- 四、源代码
-
- QWriteElement
- QLogWorker
- QLog
- 五、拓展
-
- HiLog.h
一、注册日志消息处理程序
- 调用
qInstallMessageHandler
(处理日志函数),打印调试信息(QtDebugMsg
)、警告信息(QtWarningMsg
)、严重错误(QtCriticalMsg
)和致命的错误(QtFatalMsg
)的消息
Qt
源码中会打印出很多warning
信息和debug
信息,可以通过QT_NO_WARNING_OUTPUT
和/或QT_NO_DEBUG_OUTPUT
屏蔽
- 调用qInstallMessageHandler(0)可以恢复消息处理程序。
二、日志类型
QtDebugMsg(调试信息)
- 由
qDebug()
函数生成的消息。如果未注册日志处理程序,在windows上则会发送到控制台 否则,它将被发送到调试器,
- 如果在编译过程中定义了
QT_NO_DEBUG_OUTPUT
,则此函数不执行任何操作
QtWarningMsg(警告信息)
- 由
qWarning()
函数生成,在Windows
下,消息将发送到调试器
- 如果在编译过程中定义了
QT_NO_WARNING_OUTPUT
,则此函数不执行任何操作
Qt
源码中会出现非常多的警告信息,无论是debug
还是release
都建议关
QtCriticalMsg、QtSystemMsg(关键消息、系统信息)
- 由于
qCritical()
函数生成的消息,在Windows
下,消息将发送到调试器
- 常常包含一些系统错误信息、和关键错误信息
QtFatalMsg(错误信息)
- 由于
qFatal(()
函数生成的消息,在Windows
下,消息将发送到调试器
- 常常包含一些致命的错误信息
三、自定义的日志架构
日志格式
- 日志打印格式:
[2022-06-28 10:25:43][info] 信息
- 日志文件名称:
yyyy_MM_dd.log
- 日志打印种类:
QtCriticalMsg
, QtFatalMsg
, QtInfoMsg
QtDebugMsg
(debug
模式下才存在)
- 信息打印完毕换行
信息写入逻辑
- 创建
QWriteElement
在独立线程中管理日志文件(创建、写入、关闭)
- 创建
QLogWorker
管理写入文件类型、日志路径、QWriteElement
- 创建
QLog
单例,绑定qInstallMessageHandler
,管理QLogWorker
四、源代码
QWriteElement
#ifndef QWriteElement_h
#define QWriteElement_h
#include
#include
#include
#include
class QWriteElement : public QObject
{
Q_OBJECT
public:
explicit QWriteElement(QObject *parent = nullptr);
public:
void SetFileName(const QString &fileName);
void WriteMesaage(const QString &message);
void Close();
qint64 GetLogSize() const;
signals:
void FileNameChanged(const QString &name);
void MessageReady(const QString &message);
private slots:
void OnMessageReady(const QString &mesage);
void OnFileNameChanged(const QString &fileName);
private:
QThread m_thread;
QFile *m_pLogfile;
QMutex m_lock;
};
#endif
#include "QWriteElement.h"
#include
#include
#include
QWriteElement::QWriteElement(QObject *parent) :
QObject(parent),
m_pLogfile(NULL)
{
moveToThread(&m_thread);
m_thread.start();
connect(this, &QWriteElement::FileNameChanged, this, &QWriteElement::OnFileNameChanged);;
connect(this, &QWriteElement::MessageReady, this, &QWriteElement::OnMessageReady);
}
void QWriteElement::SetFileName(const QString &fileName)
{
emit FileNameChanged(fileName);
}
void QWriteElement::WriteMesaage(const QString &message)
{
emit MessageReady(message);
}
void QWriteElement::Close()
{
m_thread.quit();
m_thread.wait(2000);
if (m_pLogfile && m_pLogfile->isOpen())
{
m_lock.lock();
m_pLogfile->close();
delete m_pLogfile;
m_pLogfile = NULL;
m_lock.unlock();
}
}
qint64 QWriteElement::GetLogSize() const
{
if (m_pLogfile)
{
return m_pLogfile->size();
}
return 0;
}
void QWriteElement::OnMessageReady(const QString &mesage)
{
m_lock.lock();
if (m_pLogfile && m_pLogfile->isOpen())
{
m_pLogfile->write(mesage.toUtf8());
m_pLogfile->flush();
}
m_lock.unlock();
}
void QWriteElement::OnFileNameChanged(const QString &fileName)
{
if (m_pLogfile)
{
QFileInfo current(*m_pLogfile);
QFileInfo dest(fileName);
if (current.absoluteFilePath() == dest.absoluteFilePath())
{
return;
}
m_pLogfile->close();
m_pLogfile = NULL;
}
QFileInfo info(fileName);
QDir dir = info.absoluteDir();
if (!dir.exists())
{
QString path = dir.absolutePath();
dir.mkpath(path);
}
m_pLogfile = new QFile(fileName);
if (m_pLogfile->open(QIODevice::WriteOnly))
{
qDebug() << "创建日志文件成功" << fileName;
}
else
{
qDebug() << "创建日志文件失败" << fileName;
}
}
QLogWorker
#ifndef QLogWorker_h
#define QLogWorker_h
#include
#include
#include
#include
#include "HiLog.h"
class QWriteElement;
class QLogWorker : public QObject
{
Q_OBJECT
public:
QLogWorker(QObject *parent = NULL);
~QLogWorker();
public:
void SetLogFlags(HiLog::eLogFlags flags);
void SetLogPath(const QString &path);
void SetLogFileMaxSize(qint64 size);
void SetLogFileCreatInterval(qint64 interval);
void SetLogFileSaveTime(qint64 time);
void WriteLog(HiLog::eLogFlag flag, const QString &message);
QList<QString> GetLogList();
QString GetLogDir();
private slots:
void OnLogTimeout();
private:
bool IsLogFileNameNeedChanged() const;
void ClearLogs();
void ClearLogsByPath(const QString &path);
private:
QString MakeFileName();
void AddWriteElement(HiLog::eLogFlag flag);
void RemoveWriteElement(HiLog::eLogFlag flag);
private:
QString m_logPath;
HiLog::eLogFlags m_eFlags;
QDateTime m_logTime;
QWriteElement* m_element;
QTimer* m_pLogTimer;
qint64 m_maxSize;
qint64 m_creatInterval;
qint64 m_saveTime;
};
#endif
#include "QLogWorker.h"
#include "QWriteElement.h"
#include
#include
#include
#include
#define MAXSIZE_LOG 100 * 1024 * 1024
#define INTERVAL_CHANGEDLOG 12 * 60 * 60
#define TIME_LOGSAVE 7 * 24 * 60 * 60
#define FORMAT_TIME_FILENAME "yyyyMMddhhmmss"
QLogWorker::QLogWorker(QObject *parent) :
QObject(parent),
m_logPath(QString()),
m_eFlags(HiLog::eLogFlags()),
m_pLogTimer(new QTimer(this)),
m_maxSize(MAXSIZE_LOG),
m_creatInterval(INTERVAL_CHANGEDLOG),
m_saveTime(TIME_LOGSAVE),
m_element(new QWriteElement())
{
#if _DEBUG
SetLogFlags(HiLog::eLogFlags(HiLog::eLog_Info | HiLog::eLog_Critical | HiLog::eLog_Fatal | HiLog::eLog_Debug));
#else
SetLogFlags(HiLog::eLogFlags(HiLog::eLog_Info | HiLog::eLog_Critical | HiLog::eLog_Fatal));
#endif
SetLogPath(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + QDir::separator() + "Log");
m_pLogTimer->setInterval(5*1000);
connect(m_pLogTimer, &QTimer::timeout, this, &QLogWorker::OnLogTimeout);
}
QLogWorker::~QLogWorker()
{
m_element->Close();
delete m_element;
m_element = nullptr;
}
void QLogWorker::SetLogFlags(HiLog::eLogFlags flags)
{
m_eFlags = flags;
}
void QLogWorker::SetLogPath(const QString &path)
{
if (m_logPath != path || IsLogFileNameNeedChanged())
{
m_logTime = QDateTime::currentDateTime();
m_logPath = path;
if (!m_pLogTimer->isActive())
{
m_pLogTimer->start();
}
m_element->SetFileName(MakeFileName());
}
}
void QLogWorker::SetLogFileMaxSize(qint64 size)
{
m_maxSize = size;
}
void QLogWorker::SetLogFileCreatInterval(qint64 interval)
{
m_creatInterval = interval;
}
void QLogWorker::SetLogFileSaveTime(qint64 time)
{
m_saveTime = time;
}
void QLogWorker::WriteLog(HiLog::eLogFlag flag, const QString &message)
{
if (m_eFlags &flag)
{
m_element->WriteMesaage(message);
}
}
QList<QString> QLogWorker::GetLogList()
{
QList<QString> filePathList;
QString dirPath = GetLogDir();
QDir dir(dirPath);
QFileInfoList fileList = dir.entryInfoList();
for each (QFileInfo fileinfo in fileList)
{
if(fileinfo.isFile())
filePathList.append(fileinfo.fileName());
}
return filePathList;
}
QString QLogWorker::GetLogDir()
{
return m_logPath;
}
void QLogWorker::OnLogTimeout()
{
if (!QFile(MakeFileName()).exists())
{
SetLogPath(m_logPath);
ClearLogs();
}
}
bool QLogWorker::IsLogFileNameNeedChanged() const
{
qint64 fileIndex = m_logTime.toMSecsSinceEpoch() / (qreal)(m_creatInterval * 1000 * 2) + 0.5;
qint64 currentIndex = QDateTime::currentMSecsSinceEpoch() / (qreal)(m_creatInterval * 1000 * 2) + 0.5;
bool timeFlag = currentIndex > fileIndex;
qint64 maxSize = qMax(m_element->GetLogSize(), maxSize);;
bool sizeFlag = maxSize > m_maxSize;
return timeFlag || sizeFlag;
}
void QLogWorker::ClearLogs()
{
ClearLogsByPath(m_logPath);
}
void QLogWorker::ClearLogsByPath(const QString &path)
{
QDir dir(path);
dir.setFilter(QDir::Files | QDir::Hidden | QDir::NoSymLinks);
dir.setSorting(QDir::Size | QDir::Reversed);
QFileInfoList list = dir.entryInfoList();
for (int i = 0; i < list.size(); ++i)
{
QFileInfo fileInfo = list.at(i);
QString fileName = fileInfo.fileName();
QDateTime logTime = QDateTime::fromString(fileName.left(fileName.indexOf(".txt")), FORMAT_TIME_FILENAME);
if (!logTime.isValid())
{
dir.remove(fileName);
}
else
{
if (QDateTime::currentMSecsSinceEpoch() - logTime.toMSecsSinceEpoch() > m_saveTime * 1000)
{
dir.remove(fileName);
}
}
}
}
QString QLogWorker::MakeFileName()
{
return m_logPath + QDir::separator() + m_logTime.toString(FORMAT_TIME_FILENAME) + "_log.txt";
}
void QLogWorker::AddWriteElement(HiLog::eLogFlag flag)
{
m_eFlags = m_eFlags | flag;
}
void QLogWorker::RemoveWriteElement(HiLog::eLogFlag flag)
{
m_eFlags = m_eFlags & ~flag;
}
QLog
#ifndef QLOG_H
#define QLog_H
#include
#include
class QLogWorker;
class QTLOG_EXPORT QLog : public QObject
{
Q_OBJECT
public:
enum eLogFlag
{
eLog_Debug = 0x001,
eLog_Info = 0x002,
eLog_Warning = 0x004,
eLog_Critical = 0x008,
eLog_Fatal = 0x010,
};
Q_DECLARE_FLAGS(eLogFlags, eLogFlag)
public:
static QLog *Instance();
static void Destroy();
static void LogMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
static void SetDefaultHandle(QtMessageHandler handler);
public:
void SetLogFlags(eLogFlags flags);
void SetLogPath(const QString &path);
QList<QString> GetLogList();
QString GetLogDir();
protected:
explicit QLog(QObject *parent = NULL);
private:
QLogWorker *Worker() const;
private:
static QMutex m_mutex;
static QMutex m_useMutex;
static QLog *m_pInstance;
static QtMessageHandler gDefaultHandler;
private:
QLogWorker *m_pWorker;
};
#endif
#include "QLog.h"
#include "QLogWorker.h"
#include
QMutex QLog::m_mutex;
QMutex QLog::m_useMutex;
QLog *QLog::m_pInstance = NULL;
QtMessageHandler QLog::gDefaultHandler = nullptr;
QLog::QLog(QObject *parent) :
QObject(parent),
m_pWorker(new QLogWorker(this))
{
}
QLogWorker *QLog::Worker() const
{
return m_pWorker;
}
QLog *QLog::Instance()
{
if (!m_pInstance)
{
m_mutex.lock();
if (!m_pInstance)
{
m_pInstance = new QLog();
}
m_mutex.unlock();
}
return m_pInstance;
}
void QLog::Destroy()
{
if (m_pInstance)
{
m_useMutex.lock();
m_mutex.lock();
delete m_pInstance;
m_pInstance = NULL;
m_mutex.unlock();
m_useMutex.unlock();
}
}
void QLog::LogMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
if (gDefaultHandler)
{
gDefaultHandler(type, context, msg);
}
QByteArray localMsg = msg.toLocal8Bit();
QString log;
eLogFlag flag;
switch (type)
{
case QtDebugMsg:
log.append(tr("[%1][调试] %2\r\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(msg));
flag = eLog_Debug;
break;
case QtInfoMsg:
log.append(tr("[%1][信息] %2\r\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(msg));
flag = eLog_Info;
break;
case QtWarningMsg:
log.append(tr("[%1][警告] %2\r\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(msg));
flag = eLog_Warning;
break;
case QtCriticalMsg:
log.append(tr("[%1][紧急] %2\r\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(msg));
flag = eLog_Critical;
break;
case QtFatalMsg:
log.append(tr("[%1][错误] %2\r\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(msg));
flag = eLog_Fatal;
break;
default:
break;
}
if (!log.isEmpty())
{
m_useMutex.lock();
QLog::Instance()->Worker()->WriteLog(flag, log);
m_useMutex.unlock();
}
}
void QLog::SetDefaultHandle(QtMessageHandler handler)
{
gDefaultHandler = handler;
}
void QLog::SetLogFlags(eLogFlags flags)
{
m_pWorker->SetLogFlags(flags);
}
void QLog::SetLogPath(const QString &path)
{
m_pWorker->SetLogPath(path);
}
QList<QString> QLog::GetLogList()
{
return m_pWorker->GetLogList();
}
QString QLog::GetLogDir()
{
return m_pWorker->GetLogDir();
}
五、拓展
打印调用函数
log.append(tr("[%1][警告] %2 %3\r\n").arg(QDateTime::currentDateTime().toString("yyyy-mm-dd hh:mm:ss")).arg(msg)).arg(context.function));
日志分类
- 可以在QLogWorker针对信息类型创建不同的QWriteElement
HiLog.h
#ifndef HILOG_H
#define HILOG_H
#include
#include
class LogWorker;
class HiLog : public QObject
{
Q_OBJECT
public:
enum eLogFlag
{
eLog_Debug = 0x001,
eLog_Info = 0x002,
eLog_Warning = 0x004,
eLog_Critical = 0x008,
eLog_Fatal = 0x010,
};
Q_DECLARE_FLAGS(eLogFlags, eLogFlag)
public:
static HiLog *Instance();
static void Destroy();
static void LogMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg);
static void SetDefaultHandle(QtMessageHandler handler);
public:
void SetLogFlags(eLogFlags flags);
void SetLogPath(const QString &path);
QList<QString> GetLogList(HiLog::eLogFlag flag);
QJsonDocument getLogMessageList(HiLog::eLogFlag flag);
QString GetLogDir(HiLog::eLogFlag flag);
bool deleteLogFile(QString fileName);
void clearAllLogs();
protected:
explicit HiLog(QObject *parent = NULL);
private:
LogWorker *Worker() const;
private:
static QMutex m_mutex;
static QMutex m_useMutex;
static HiLog *m_pInstance;
static QtMessageHandler gDefaultHandler;
private:
LogWorker *m_pWorker;
};
#endif