金歌 发表于 2024-2-15 16:26:09

使用C++和QT实现Log自定义日志系统

MyLog

说明

https://img2023.cnblogs.com/blog/3322874/202312/3322874-20231212182006160-1407270665.png


[*]使用QT的qInstallMessageHandler函数结合qDebug,qInfo实现自定义的日志系统
[*]输出日志到文件和控制台
[*]自动检测日志文件大小
[*]自动更新日志文件修改日期
[*]自动备份
[*]自动删除一个月前的日志文件
[*]支持多线程程序
[*]支持扩展,可输出日志到数据库,网络,或服务器
[*]支持扩展,可使用config文件进行配置
警告



[*]注:博主所有资源永久免费,若有帮助,请点赞转发是对我莫大的帮助
[*]注:博主本人学习过程的分享,引用他人的文章皆会标注原作者
[*]注:本人文章非盈利性质,若有侵权请联系我删除
[*]注:获取资源或者咨询问题请联系Q:2950319782
[*]注:博主本人很菜,文章基本是二次创作,大佬请忽略我的随笔
[*]注:我会一步步分享实现的细节,若仍有问题联系我
开发环境



[*]win10系统
[*]qtcreator4.11.1
[*]C++11
[*]QT5.14.2
GitHub



[*]GitHub下 的Log文件
[*]若不能访问GitHub,源码的资源包会随文章同步发布,免费下载
[*]资源包较GitHub更新不及时,请谅解
问题解决



[*]Qt自定义日志类
需求



[*]输出日志信息到日志文件
[*]更新日志的修改日期
[*]日志文件超过一定大小备份老的创建新的
[*]删除一个月前的日志文件
结构

https://img2023.cnblogs.com/blog/3322874/202312/3322874-20231212182016256-88160309.png
https://img2023.cnblogs.com/blog/3322874/202312/3322874-20231212182027907-1941780479.png
思路



[*]随便创建一个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_HMyLog.cpp

<blockquote>#include "mylog.h"#include#include#define LOG 1//初始化静态变量QMutex MyLog::logMutex;QFile* MyLog::logFile = NULL;QTextStream* MyLog::logOut = NULL;QScopedPointer 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(){    //设置日志文件夹的路径,./exe    logDir.setPath("log");    //获取日志的绝对路径    QString logPath = logDir.absoluteFilePath("today.log");    //获取日志文件创建的时间    //保存日志文件最后的修改时间    logFileCreateDate = QFileInfo(logPath).lastModified().date();    //打开并备份日志文件    openAndBackupLogFile();    //每10分钟检查一次日志文件创建的时间    renameLogFileTimer.setInterval(1000 * 60 *1000);    renameLogFileTimer.start();    //处理超时事件,10分钟重复一次    QObject::connect(&renameLogFileTimer,&QTimer::timeout,(){      QMutexLocker locker(&MyLog::logMutex);      openAndBackupLogFile();      checkLogFiles();      autoDeleteLog();    });    //定时刷新日志输出到日志文件,1秒1刷新    flushLogFileTimer.setInterval(1000);    flushLogFileTimer.start();    QObject::connect(&flushLogFileTimer,&QTimer::timeout,[](){#if LOG      // 测试不停地写入当前时间到日志文件      qDebug() flush();      }    });}MyLog::~MyLog(){    if(NULL != logFile)    {      logFile->flush();      logFile->close();      logOut = NULL;      logFile = NULL;    }}//打开并备份之前的日志文件void MyLog::openAndBackupLogFile(){    //有可能一天多次打开日志文件,使用追加的方式打开    //目录不存在,创建目录    if(!logDir.exists())    {      logDir.mkpath(".");    }    //log.txt的路径    QString logPath = logDir.absoluteFilePath("today.log");    //程序启动的时候,logfile为空    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();      }    }    //程序运行的时候,创建日期不是当前日期,更新日期,重命名,备份老的并生成新的log.txt    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);      //创建新的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,备份并重新创建日志文件    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();    //基准,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());      }    }}//定义消息处理函数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
页: [1]
查看完整版本: 使用C++和QT实现Log自定义日志系统