利用C++和QT實(shí)現(xiàn)Log自定義日志系統(tǒng)
說明
- 使用QT的qInstallMessageHandler函數(shù)結(jié)合qDebug,qInfo實(shí)現(xiàn)自定義的日志系統(tǒng)
- 輸出日志到文件和控制臺(tái)
- 自動(dòng)檢測(cè)日志文件大小
- 自動(dòng)更新日志文件修改日期
- 自動(dòng)備份
- 自動(dòng)刪除一個(gè)月前的日志文件
- 支持多線程程序
- 支持?jǐn)U展,可輸出日志到數(shù)據(jù)庫,網(wǎng)絡(luò),或服務(wù)器
- 支持?jǐn)U展,可使用config文件進(jìn)行配置
開發(fā)環(huán)境
win10系統(tǒng)
qtcreator4.11.1
C++11
QT5.14.2
GitHub
若不能訪問GitHub,源碼的資源包會(huì)隨文章同步發(fā)布,免費(fèi)下載
資源包較GitHub更新不及時(shí),請(qǐng)諒解
問題解決
需求
輸出日志信息到日志文件
更新日志的修改日期
日志文件超過一定大小備份老的創(chuàng)建新的
刪除一個(gè)月前的日志文件
結(jié)構(gòu)
思路
- 隨便創(chuàng)建一個(gè)widget程序,放個(gè)測(cè)試按鈕
- 主要思路是使用 qInstallMessageHandler()接管qDebug(), qWarning()等調(diào)試信息,然后將信息流存儲(chǔ)至本地日志文件,并管理日志文件
- 先創(chuàng)建一個(gè)MyLog的類,在這里面我們實(shí)現(xiàn)自定義的日志系統(tǒng)
- 這里依然是使用單例實(shí)現(xiàn),整個(gè)程序的日志應(yīng)該只能有一個(gè)
- 首先實(shí)現(xiàn)單例getInstance獲取MyLog的實(shí)例
- 下面處理MyLog的構(gòu)造函數(shù),每一次啟動(dòng)日志系統(tǒng),都要先設(shè)置日志文件的路徑,然后更新修改日期,然后打開并備份老的日志文件,打開之后,每10分鐘刷新日志文件,每1秒都將信息輸出到日志,
- 下面實(shí)現(xiàn)這個(gè)打開并備份老的日志文件的功能openAndBackupLogFile,這里我們的日志文件以天為單位,先處理一天內(nèi)多次啟動(dòng)日志系統(tǒng)的情況,以追加的方式寫入到日志文件里即可;如果程序運(yùn)行的時(shí)候,日志系統(tǒng)的日期和程序運(yùn)行日期不統(tǒng)一,同步日志日期為程序運(yùn)行日期,生成新的日期日志,并且備份老的日志
- 然后實(shí)現(xiàn)處理日志文件過大的問題,只要日志文件超限,備份老的,創(chuàng)建新的即可
- 然后實(shí)現(xiàn)自動(dòng)刪除超時(shí)的日志文件,每次啟動(dòng)日志系統(tǒng)的時(shí)候,以當(dāng)前時(shí)間為基準(zhǔn),計(jì)算出1個(gè)月前的時(shí)間,遍歷日志目錄下的所有日志文件,因?yàn)槿罩疚募家詴r(shí)間命名,刪除超過1個(gè)月的日志文件即可
- 最后,我們只需要處理信息函數(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; //重命名日志文件使用的定時(shí)器 QTimer renameLogFileTimer; //刷新輸出到日志文件的定時(shí)器 QTimer flushLogFileTimer; //日志文件的創(chuàng)建時(shí)間 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)建實(shí)例 if(self.isNull()) { //加把鎖,只能有一個(gè)線程訪問 static QMutex mutex; //自動(dòng)加解鎖 QMutexLocker locker(&mutex); //再次判斷有沒有實(shí)例,防止等待的時(shí)間中有線程獲取到實(shí)例了 if(self.isNull()) { self.reset(new MyLog); } } return self.data(); } MyLog::MyLog() { //設(shè)置日志文件夾的路徑,./exe logDir.setPath("log"); //獲取日志的絕對(duì)路徑 QString logPath = logDir.absoluteFilePath("today.log"); //獲取日志文件創(chuàng)建的時(shí)間 //保存日志文件最后的修改時(shí)間 logFileCreateDate = QFileInfo(logPath).lastModified().date(); //打開并備份日志文件 openAndBackupLogFile(); //每10分鐘檢查一次日志文件創(chuàng)建的時(shí)間 renameLogFileTimer.setInterval(1000 * 60 *1000); renameLogFileTimer.start(); //處理超時(shí)事件,10分鐘重復(fù)一次 QObject::connect(&renameLogFileTimer,&QTimer::timeout,[this](){ QMutexLocker locker(&MyLog::logMutex); openAndBackupLogFile(); checkLogFiles(); autoDeleteLog(); }); //定時(shí)刷新日志輸出到日志文件,1秒1刷新 flushLogFileTimer.setInterval(1000); flushLogFileTimer.start(); QObject::connect(&flushLogFileTimer,&QTimer::timeout,[](){ #if LOG // 測(cè)試不停地寫入當(dāng)前時(shí)間到日志文件 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"); //程序啟動(dòng)的時(shí)候,logfile為空 if(logFile == NULL) { //創(chuàng)建新的 logFile = new QFile(logPath); //只寫,追加的方式打開日志文件 //成功,創(chuàng)建文本流對(duì)象與日志文件關(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(); } } //程序運(yùn)行的時(shí)候,創(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,進(jìn)行更新 //只寫,截?cái)嗟姆绞酱蜷_日志 logFile = new QFile(logPath); logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) : NULL; //更新為修改時(shí)間 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"); } } } //自動(dòng)刪除超過時(shí)間的日志文件 void MyLog::autoDeleteLog() { //當(dāng)前時(shí)間 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; } //將文件名解析為日期對(duì)象 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 //輸出到控制臺(tái) 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實(shí)現(xiàn)Log自定義日志系統(tǒng)的詳細(xì)內(nèi)容,更多關(guān)于C++ QT自定義日志系統(tǒng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
OpenCV 視頻中火焰檢測(cè)識(shí)別實(shí)踐
本文主要介紹了OpenCV 視頻中火焰檢測(cè)識(shí)別,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09C/C++ 中const關(guān)鍵字的用法小結(jié)
C++中的const關(guān)鍵字的用法非常靈活,而使用const將大大改善程序的健壯性。這篇文章主要介紹了C/C++ 中const關(guān)鍵字的用法,需要的朋友可以參考下2020-02-02OpenCV實(shí)現(xiàn)鼠標(biāo)在圖像上框選單目標(biāo)和多目標(biāo)
這篇文章主要為大家詳細(xì)介紹了OpenCV實(shí)現(xiàn)鼠標(biāo)在圖像上框選單目標(biāo)和多目標(biāo),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-08-08Qt中Tab與Tree組件實(shí)現(xiàn)分頁菜單
本文主要介紹tabWidget選擇夾組件與TreeWidget樹形選擇組件的常用方法及靈活運(yùn)用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12