MyLog

说明

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

警告

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

开发环境

  • win10系统
  • qtcreator4.11.1
  • C++11
  • QT5.14.2

GitHub

  • GitHub下 的Log文件
  • 若不能访问GitHub,源码的资源包会随文章同步发布,免费下载
  • 资源包较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_H

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()
{
//设置日志文件夹的路径,./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,[this](){
QMutexLocker locker(&MyLog::logMutex);
openAndBackupLogFile();
checkLogFiles();
autoDeleteLog();
}); //定时刷新日志输出到日志文件,1秒1刷新
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(".");
}
//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 << 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实现Log自定义日志系统的更多相关文章

  1. 2.1 自定义日志系统-log4net

    说明 Prism中如果把日志级别设定为DEBUG,会显示框架加载信息 Prism默认是没有日志系统的 步骤 下载log4net包 Install-Package log4net; 在app.confi ...

  2. Django中配置自定义日志系统

  3. c#自定义日志记录

    废话不多说,直接上代码: 很简单:将类复制到项目中,最后在配置文件上配置一下:logUrl即可. 默认保存在:项目/temp/log /// <summary> /// 日志类 /// & ...

  4. log4j分离日志输出 自定义过滤 自定义日志文件

    普通的log4j.properties 定义: ### set log levels ### log4j.rootLogger = debug,D,E ## Disable other log log ...

  5. YII2 自定义日志路径

    YII 提供的日志写入方法: 1.Yii::getLogger()->log($message, $level, $category = 'application') 2.Yii::trace( ...

  6. 转:NLog 自定义日志内容,写日志到数据库;修改Nlog.config不起作用的原因

    转:http://www.cnblogs.com/tider1999/p/4308440.html NLog的安装请百度,我安装的是3.2.NLog可以向文件,数据库,邮件等写日志,想了解请百度,这里 ...

  7. Java自定义日志输出文件

    Java自定义日志输出文件 日志的打印,在程序中是必不可少的,如果需要将不同的日志打印到不同的地方,则需要定义不同的Appender,然后定义每一个Appender的日志级别.打印形式和日志的输出路径 ...

  8. QT日志系统

    今天给大家介绍下QT里面的日志系统,直接上代码 #include <QApplication> #include <iostream> #include "ThorH ...

  9. Python3自定义日志类教程

    一.说明 Python3的logging功能是比较丰富的支持不同层次的日志输出,但或是我们想在日志前输出时间.或是我们想要将日志输入到文件,我们还是想要自定义日志类. 之前自己也尝试写过但感觉文档太乱 ...

  10. ELK收集Nginx自定义日志格式输出

    1.ELK收集日志的有两种常用的方式: 1.1:不修改源日志格式,简单的说就是在logstash中转通过 grok方式进行过滤处理,将原始无规则的日志转换为规则日志(Logstash自定义日志格式) ...

随机推荐

  1. 文心一言 VS 讯飞星火 VS chatgpt (80)-- 算法导论7.4 5题

    五.如果用go语言,当输入数据已经"几乎有序"时,插入排序速度很快.在实际应用中,我们可以利用这一特点来提高快速排序的速度.当对一个长度小于 k 的子数组调用快速排序时,让它不做任 ...

  2. Unity 游戏开发、01 基础知识大全、简单功能脚本实现

    2.3 窗口布局 Unity默认窗口布局 Hierarchy 层级窗口 Scene 场景窗口,3D视图窗口 Game 游戏播放窗口 Inspector 检查器窗口,属性窗口 Project 项目窗口 ...

  3. Arrays.asList():使用指南

    Arrays.asList() 是一个 Java 的静态方法,它可以把一个数组或者多个参数转换成一个 List 集合.这个方法可以作为数组和集合之间的桥梁,方便我们使用集合的一些方法和特性.本文将介绍 ...

  4. Solution Set -「ARC 111」

    「ARC 111A」Simple Math 2 Link. \(\lfloor \frac{10^N - kM^2}{M} \rfloor \equiv \lfloor \frac{10^N}{M} ...

  5. Cplex混合整数规划求解(Python API)

    绝对的原创!罕见的Cplex-Python API混合整数规划求解教程!这是我盯了一天的程序一条条写注释一条条悟出来的•́‸ก 一.问题描述 求解有容量限制的的设施位置问题,使用Benders分解.模 ...

  6. MySQL系列之——MySQL体系结构、基础管理(用户、权限管理、连接管理、多种启动方式介绍、初始化配置、多实例的应用)

    文章目录 一 体系结构 1.1 C/S(客户端/服务端)模型介绍 1.2 实例介绍 1.3 mysqld程序运行原理 1.3.1 mysqld程序结构 1.3.2 一条SQL语句的执行过程 1.3.2 ...

  7. 如何使用Java创建数据透视表并导出为PDF

    摘要:本文由葡萄城技术团队原创并首发.转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 前言 数据透视分析是一种强大的工具,可以帮助我们从大量数据中提取有用信 ...

  8. 第六单元《管理学进展》单元测试 mooc

    第六单元<管理学进展>单元测试 返回 本次得分为:10.00/10.00, 本次测试的提交时间为:2020-08-30, 如果你认为本次测试成绩不理想,你可以选择 再做一次 . 1 判断( ...

  9. RK3588平台产测之ArmSoM-W3软硬件重启测试

    1. 简介 专栏总目录 ArmSoM团队在产品量产之前都会对产品做几次专业化的功能测试以及性能压力测试,以此来保证产品的质量以及稳定性 优秀的产品都要进行多次全方位的功能测试以及性能压力测试才能够经得 ...

  10. 研发日常踩坑-Mysql分页数据重复

    踩坑描述: 写分页查询接口,order by和limit混用的时候,出现了排序的混乱情况 在进行第N页查询时,出现与第一前面页码的数据一样的记录. 问题 在MySQL中分页查询,我们经常会用limit ...