MyLog
说明

- 使用QT的qInstallMessageHandler函数结合qDebug,qInfo实现自定义的日志系统
- 输出日志到文件和控制台
- 自动检测日志文件大小
- 自动更新日志文件修改日期
- 自动备份
- 自动删除一个月前的日志文件
- 支持多线程程序
- 支持扩展,可输出日志到数据库,网络,或服务器
- 支持扩展,可使用config文件进行配置
警告
- 注:博主所有资源永久免费,若有帮助,请点赞转发是对我莫大的帮助
- 注:博主本人学习过程的分享,引用他人的文章皆会标注原作者
- 注:本人文章非盈利性质,若有侵权请联系我删除
- 注:获取资源或者咨询问题请联系Q:2950319782
- 注:博主本人很菜,文章基本是二次创作,大佬请忽略我的随笔
- 注:我会一步步分享实现的细节,若仍有问题联系我
开发环境
- win10系统
- qtcreator4.11.1
- C++11
- QT5.14.2
GitHub
问题解决
需求
- 输出日志信息到日志文件
- 更新日志的修改日期
- 日志文件超过一定大小备份老的创建新的
- 删除一个月前的日志文件
结构
思路
- 随便创建一个widget程序,放个测试按钮
- 主要思路是使用 qInstallMessageHandler()接管qDebug(), qWarning()等调试信息,然后将信息流存储至本地日志文件,并管理日志文件
- 先创建一个MyLog的类,在这里面我们实现自定义的日志系统
- 这里依然是使用单例实现,整个程序的日志应该只能有一个
- 首先实现单例getInstance获取MyLog的实例
- 下面处理MyLog的构造函数,每一次启动日志系统,都要先设置日志文件的路径,然后更新修改日期,然后打开并备份老的日志文件,打开之后,每10分钟刷新日志文件,每1秒都将信息输出到日志,
- 下面实现这个打开并备份老的日志文件的功能openAndBackupLogFile,这里我们的日志文件以天为单位,先处理一天内多次启动日志系统的情况,以追加的方式写入到日志文件里即可;如果程序运行的时候,日志系统的日期和程序运行日期不统一,同步日志日期为程序运行日期,生成新的日期日志,并且备份老的日志
- 然后实现处理日志文件过大的问题,只要日志文件超限,备份老的,创建新的即可
- 然后实现自动删除超时的日志文件,每次启动日志系统的时候,以当前时间为基准,计算出1个月前的时间,遍历日志目录下的所有日志文件,因为日志文件都以时间命名,删除超过1个月的日志文件即可
- 最后,我们只需要处理信息函数即可,捕获系统中的各种输出信息,输出到文件即可
关键代码
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();
static void messageHandler(QtMsgType type,
const QMessageLogContext& context,
const QString& msg);
public:
void openAndBackupLogFile();
void checkLogFiles();
void autoDeleteLog();
void installMessageHandler();
void uninstallMessageHandler();
private:
QDir logDir;
QTimer renameLogFileTimer;
QTimer flushLogFileTimer;
QDate logFileCreateDate;
static QFile* logFile;
static QTextStream* logOut;
static QMutex logMutex;
static QScopedPointer<MyLog> self;
};
#endif
MyLog.cpp
#include "mylog.h"
#include<QDebug>
#include<QTextCodec>
#define LOG 1
QMutex MyLog::logMutex;
QFile* MyLog::logFile = NULL;
QTextStream* MyLog::logOut = NULL;
QScopedPointer<MyLog> MyLog::self;
MyLog* MyLog::getInstance()
{
if(self.isNull())
{
static QMutex mutex;
QMutexLocker locker(&mutex);
if(self.isNull())
{
self.reset(new MyLog);
}
}
return self.data();
}
MyLog::MyLog()
{
logDir.setPath("log");
QString logPath = logDir.absoluteFilePath("today.log");
logFileCreateDate = QFileInfo(logPath).lastModified().date();
openAndBackupLogFile();
renameLogFileTimer.setInterval(1000 * 60 *1000);
renameLogFileTimer.start();
QObject::connect(&renameLogFileTimer,&QTimer::timeout,[this](){
QMutexLocker locker(&MyLog::logMutex);
openAndBackupLogFile();
checkLogFiles();
autoDeleteLog();
});
flushLogFileTimer.setInterval(1000);
flushLogFileTimer.start();
QObject::connect(&flushLogFileTimer,&QTimer::timeout,[](){
#if LOG
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()
{
if(!logDir.exists())
{
logDir.mkpath(".");
}
QString logPath = logDir.absoluteFilePath("today.log");
if(logFile == NULL)
{
logFile = new QFile(logPath);
logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text |QIODevice::Append)) ? new QTextStream(logFile) : NULL;
if(logOut != NULL)
{
logOut->setCodec("UTF-8");
}
if(logFileCreateDate.isNull())
{
logFileCreateDate = QDate::currentDate();
}
}
if(logFileCreateDate != QDate::currentDate())
{
logFile->flush();
logFile->close();
QString backUpLogPath = logDir.absoluteFilePath(logFileCreateDate.toString("yyyy-MM-dd.log"));;
QFile::copy(logPath,backUpLogPath);
QFile::remove(logPath);
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()
{
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);
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()
{
QDateTime now = QDateTime::currentDateTime();
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");
if(dateTime2 < dateTime1)
{
dir.remove(f.absoluteFilePath());
}
}
}
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);
}