利用C++和QT實現(xiàn)Log自定義日志系統(tǒng)
更新時間:2023年12月13日 08:41:03 作者:KanHai1024
這篇文章主要為大家詳細介紹了如何利用C++和QT實現(xiàn)Log自定義日志系統(tǒng),文中的示例代碼講解詳細,具有一定的借鑒價值,有需要的小伙伴可以參考下
說明

- 使用QT的qInstallMessageHandler函數(shù)結(jié)合qDebug,qInfo實現(xiàn)自定義的日志系統(tǒng)
- 輸出日志到文件和控制臺
- 自動檢測日志文件大小
- 自動更新日志文件修改日期
- 自動備份
- 自動刪除一個月前的日志文件
- 支持多線程程序
- 支持擴展,可輸出日志到數(shù)據(jù)庫,網(wǎng)絡(luò),或服務(wù)器
- 支持擴展,可使用config文件進行配置
開發(fā)環(huán)境
win10系統(tǒng)
qtcreator4.11.1
C++11
QT5.14.2
GitHub
若不能訪問GitHub,源碼的資源包會隨文章同步發(fā)布,免費下載
資源包較GitHub更新不及時,請諒解
問題解決
需求
輸出日志信息到日志文件
更新日志的修改日期
日志文件超過一定大小備份老的創(chuàng)建新的
刪除一個月前的日志文件
結(jié)構(gòu)


思路
- 隨便創(chuàng)建一個widget程序,放個測試按鈕
- 主要思路是使用 qInstallMessageHandler()接管qDebug(), qWarning()等調(diào)試信息,然后將信息流存儲至本地日志文件,并管理日志文件
- 先創(chuàng)建一個MyLog的類,在這里面我們實現(xiàn)自定義的日志系統(tǒng)
- 這里依然是使用單例實現(xiàn),整個程序的日志應(yīng)該只能有一個
- 首先實現(xiàn)單例getInstance獲取MyLog的實例
- 下面處理MyLog的構(gòu)造函數(shù),每一次啟動日志系統(tǒng),都要先設(shè)置日志文件的路徑,然后更新修改日期,然后打開并備份老的日志文件,打開之后,每10分鐘刷新日志文件,每1秒都將信息輸出到日志,
- 下面實現(xiàn)這個打開并備份老的日志文件的功能openAndBackupLogFile,這里我們的日志文件以天為單位,先處理一天內(nèi)多次啟動日志系統(tǒng)的情況,以追加的方式寫入到日志文件里即可;如果程序運行的時候,日志系統(tǒng)的日期和程序運行日期不統(tǒng)一,同步日志日期為程序運行日期,生成新的日期日志,并且備份老的日志
- 然后實現(xiàn)處理日志文件過大的問題,只要日志文件超限,備份老的,創(chuàng)建新的即可
- 然后實現(xiàn)自動刪除超時的日志文件,每次啟動日志系統(tǒng)的時候,以當(dāng)前時間為基準(zhǔn),計算出1個月前的時間,遍歷日志目錄下的所有日志文件,因為日志文件都以時間命名,刪除超過1個月的日志文件即可
- 最后,我們只需要處理信息函數(shù)即可,捕獲系統(tǒng)中的各種輸出信息,輸出到文件即可
關(guān)鍵代碼
MyLog.h
#ifndef MYLOG_H
#define MYLOG_H
#include <iostream>
#include <QDateTime>
#include <QMutexLocker>
#include <QDir>
#include <QTimer>
#include <QTextStream>
//最大保存文件大小
const int g_logLimitSize = 5;
class MyLog
{
public:
MyLog();
~MyLog();
static MyLog* getInstance();
//消息處理函數(shù)
static void messageHandler(QtMsgType type,
const QMessageLogContext& context,
const QString& msg);
public:
//打開并備份之前的日志文件
void openAndBackupLogFile();
void checkLogFiles();
void autoDeleteLog();
//安裝消息處理函數(shù)
void installMessageHandler();
//卸載消息處理函數(shù),并釋放資源
void uninstallMessageHandler();
private:
//日志文件夾目錄
QDir logDir;
//重命名日志文件使用的定時器
QTimer renameLogFileTimer;
//刷新輸出到日志文件的定時器
QTimer flushLogFileTimer;
//日志文件的創(chuàng)建時間
QDate logFileCreateDate;
//日志文件
static QFile* logFile;
//輸出日志
static QTextStream* logOut;
//日志鎖
static QMutex logMutex;
static QScopedPointer<MyLog> self;
};
#endif // MYLOG_H
MyLog.cpp
#include "mylog.h"
#include<QDebug>
#include<QTextCodec>
#define LOG 1
//初始化靜態(tài)變量
QMutex MyLog::logMutex;
QFile* MyLog::logFile = NULL;
QTextStream* MyLog::logOut = NULL;
QScopedPointer<MyLog> MyLog::self;
//定義單例模式
MyLog* MyLog::getInstance()
{
//還沒有創(chuàng)建實例
if(self.isNull())
{
//加把鎖,只能有一個線程訪問
static QMutex mutex;
//自動加解鎖
QMutexLocker locker(&mutex);
//再次判斷有沒有實例,防止等待的時間中有線程獲取到實例了
if(self.isNull())
{
self.reset(new MyLog);
}
}
return self.data();
}
MyLog::MyLog()
{
//設(shè)置日志文件夾的路徑,./exe
logDir.setPath("log");
//獲取日志的絕對路徑
QString logPath = logDir.absoluteFilePath("today.log");
//獲取日志文件創(chuàng)建的時間
//保存日志文件最后的修改時間
logFileCreateDate = QFileInfo(logPath).lastModified().date();
//打開并備份日志文件
openAndBackupLogFile();
//每10分鐘檢查一次日志文件創(chuàng)建的時間
renameLogFileTimer.setInterval(1000 * 60 *1000);
renameLogFileTimer.start();
//處理超時事件,10分鐘重復(fù)一次
QObject::connect(&renameLogFileTimer,&QTimer::timeout,[this](){
QMutexLocker locker(&MyLog::logMutex);
openAndBackupLogFile();
checkLogFiles();
autoDeleteLog();
});
//定時刷新日志輸出到日志文件,1秒1刷新
flushLogFileTimer.setInterval(1000);
flushLogFileTimer.start();
QObject::connect(&flushLogFileTimer,&QTimer::timeout,[](){
#if LOG
// 測試不停地寫入當(dāng)前時間到日志文件
qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
#endif
//刷新
QMutexLocker locker(&MyLog::logMutex);
if(NULL != logOut)
{
logOut->flush();
}
});
}
MyLog::~MyLog()
{
if(NULL != logFile)
{
logFile->flush();
logFile->close();
logOut = NULL;
logFile = NULL;
}
}
//打開并備份之前的日志文件
void MyLog::openAndBackupLogFile()
{
//有可能一天多次打開日志文件,使用追加的方式打開
//目錄不存在,創(chuàng)建目錄
if(!logDir.exists())
{
logDir.mkpath(".");
}
//log.txt的路徑
QString logPath = logDir.absoluteFilePath("today.log");
//程序啟動的時候,logfile為空
if(logFile == NULL)
{
//創(chuàng)建新的
logFile = new QFile(logPath);
//只寫,追加的方式打開日志文件
//成功,創(chuàng)建文本流對象與日志文件關(guān)聯(lián),向日志文件寫內(nèi)容
logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text |QIODevice::Append)) ? new QTextStream(logFile) : NULL;
if(logOut != NULL)
{
//設(shè)置編碼格式
logOut->setCodec("UTF-8");
}
//日志文件第一次創(chuàng)建,創(chuàng)建日期無效,設(shè)置為修改日期
if(logFileCreateDate.isNull())
{
logFileCreateDate = QDate::currentDate();
}
}
//程序運行的時候,創(chuàng)建日期不是當(dāng)前日期,更新日期,重命名,備份老的并生成新的log.txt
if(logFileCreateDate != QDate::currentDate())
{
//先刷新緩沖區(qū),確保內(nèi)容先輸出到文件里
logFile->flush();
logFile->close();
//更新日期到備份文件
QString backUpLogPath = logDir.absoluteFilePath(logFileCreateDate.toString("yyyy-MM-dd.log"));;
//備份原來的日志
QFile::copy(logPath,backUpLogPath);
//刪除原來的日志文件
QFile::remove(logPath);
//創(chuàng)建新的log.txt,進行更新
//只寫,截斷的方式打開日志
logFile = new QFile(logPath);
logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) : NULL;
//更新為修改時間
logFileCreateDate = QDate::currentDate();
if(logOut != NULL)
{
logOut->setCodec("UTF-8");
}
}
}
//檢查文件大小
void MyLog::checkLogFiles()
{
//日志文件大小超過5m,備份并重新創(chuàng)建日志文件
if(logFile->size() > 1024* g_logLimitSize)
{
//清空緩沖
logFile->flush();
logFile->close();
QString logPath = logDir.absoluteFilePath("today.log");
//備份老的日志文件
QString backUplogPath = logDir.absoluteFilePath(logFileCreateDate.toString("yyyy-MM-dd.log"));
QFile::copy(logPath,backUplogPath);
QFile::remove(logPath);
//創(chuàng)建新的日志文件
logFile = new QFile(logPath);
logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) :NULL;
logFileCreateDate = QDate::currentDate();
if(logOut != NULL)
{
logOut->setCodec("UTF-8");
}
}
}
//自動刪除超過時間的日志文件
void MyLog::autoDeleteLog()
{
//當(dāng)前時間
QDateTime now = QDateTime::currentDateTime();
//基準(zhǔn),30天前
QDateTime dateTime1 = now.addDays(-30);
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");
//大于30天,刪除
if(dateTime2 < dateTime1)
{
dir.remove(f.absoluteFilePath());
}
}
}
//定義消息處理函數(shù)
void MyLog::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QMutexLocker locker(&MyLog::logMutex);
QString level;
switch (type) {
case QtDebugMsg:
level = "DEBUG";
break;
case QtInfoMsg:
level = "INFO";
break;
case QtWarningMsg:
level = "WARN";
break;
case QtCriticalMsg:
level = "ERROR";
break;
case QtFatalMsg:
level = "FATAL";
break;
default:
break;
}
#if defined (Q_OS_WIN)
QByteArray localMsg = QTextCodec::codecForName("GB2312")->fromUnicode(msg);
#else
QByteArray localMsg = msg.toLocal8Bit();
#endif
//輸出到控制臺
std::cout << std::string(localMsg) << std::endl;
if(NULL == MyLog::logOut)
{
return;
}
//輸出到日志文件
//獲取文件名,去掉路徑
QString fileName = context.file;
int index = fileName.lastIndexOf(QDir::separator());
fileName = fileName.mid(index + 1);
//寫入日志信息
(*MyLog::logOut) << 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);
}
//安裝
void MyLog::installMessageHandler()
{
qInstallMessageHandler(MyLog::messageHandler);
}
//卸載
void MyLog::uninstallMessageHandler()
{
qInstallMessageHandler(NULL);
}以上就是利用C++和QT實現(xiàn)Log自定義日志系統(tǒng)的詳細內(nèi)容,更多關(guān)于C++ QT自定義日志系統(tǒng)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C/C++ 中const關(guān)鍵字的用法小結(jié)
C++中的const關(guān)鍵字的用法非常靈活,而使用const將大大改善程序的健壯性。這篇文章主要介紹了C/C++ 中const關(guān)鍵字的用法,需要的朋友可以參考下2020-02-02
OpenCV實現(xiàn)鼠標(biāo)在圖像上框選單目標(biāo)和多目標(biāo)
這篇文章主要為大家詳細介紹了OpenCV實現(xiàn)鼠標(biāo)在圖像上框選單目標(biāo)和多目標(biāo),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-08-08

