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. 强化学习 Proximal Policy Optimization (PPO)

    参考: 李宏毅老师课件 PPO = Policy Gradient 从 On-policy 到 Off-policy, 再加一些constraint Policy Gradient Basic Con ...

  2. 详谈 springboot整合shiro

    背景: 上文学习了shrio 基本概念后,本章将进一步的落地实践学习,在springboot中如何去整合shrio,整个过程步骤有个清晰的了解. 利用Shiro进行登录认证主要步骤: 1. 添加依赖: ...

  3. Python 基础面试第三弹

    1. 获取当前目录下所有文件名 import os def get_all_files(directory): file_list = [] # os.walk返回一个生成器,每次迭代时返回当前目录路 ...

  4. redhat7查找已接网线但是还未配置IP的网卡接口

    方法一:nmcli 输出中参数WIRED-PROPERTIES.CARRIER为on即为接网线网卡 #nmcli device show |grep -i -E "device|carrie ...

  5. 【matplotlib基础】--图例

    Matplotlib 中的图例是帮助观察者理解图像数据的重要工具.图例通常包含在图像中,用于解释不同的颜色.形状.标签和其他元素. 1. 主要参数 当不设置图例的参数时,默认的图例是这样的. impo ...

  6. C# winform 无边框窗口 移动

    给自己留个笔记, 在用wke做界面的时候. 往往需要把winform窗口设置成无边框 但是WebUI也需要移动窗口, 所以才把以前在易语言中用的方法翻译过来使用 第零步: 设置无边框窗口 form属性 ...

  7. 当开源项目 Issue 遇到了 DevChat

    目录 1. 概述 2. Bug 分析与复现 3. Bug 定位与修复 4. 代码测试 5. 文档更新 6. 提交 Commit 7. 总结 1. 概述 没错,又有人给 GoPool 项目提 issue ...

  8. 自定义注解实现数据序列化时进行数据脱敏(基于springboot默认jackjson)、消息转换器HttpMessageConverter

    消息转换器 HttpMessageConverter 消息转化器的作用 将请求报文转化为Java对象 将Java对象转化为响应报文 消息转换器接口 public interface HttpMessa ...

  9. 基于TOTP算法的Github两步验证2FA(双因子)机制Python3.10实现

    从今年(2023)三月份开始,Github开始强制用户开启两步验证2FA(双因子)登录验证,毫无疑问,是出于安全层面的考虑,毕竟Github账号一旦被盗,所有代码仓库都会毁于一旦,关于双因子登录的必要 ...

  10. os --- 多种操作系统接口¶

    os.path --- 常用路径操作 源代码: Lib/posixpath.py (用于 POSIX) 和 Lib/ntpath.py (用于 Windows). 此模块实现了一些有用的路径名称相关函 ...