经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
使用C++和QT实现Log自定义日志系统
来源:cnblogs  作者:KanHai1024  时间:2023/12/13 9:10:07  对本文有异议

MyLog

说明

image

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

警告

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

开发环境

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

GitHub

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

问题解决

需求

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

结构

image

image

思路

  • 随便创建一个widget程序,放个测试按钮
  • 主要思路是使用 qInstallMessageHandler()接管qDebug(), qWarning()等调试信息,然后将信息流存储至本地日志文件,并管理日志文件
  • 先创建一个MyLog的类,在这里面我们实现自定义的日志系统
  • 这里依然是使用单例实现,整个程序的日志应该只能有一个
  • 首先实现单例getInstance获取MyLog的实例
  • 下面处理MyLog的构造函数,每一次启动日志系统,都要先设置日志文件的路径,然后更新修改日期,然后打开并备份老的日志文件,打开之后,每10分钟刷新日志文件,每1秒都将信息输出到日志,
  • 下面实现这个打开并备份老的日志文件的功能openAndBackupLogFile,这里我们的日志文件以天为单位,先处理一天内多次启动日志系统的情况,以追加的方式写入到日志文件里即可;如果程序运行的时候,日志系统的日期和程序运行日期不统一,同步日志日期为程序运行日期,生成新的日期日志,并且备份老的日志
  • 然后实现处理日志文件过大的问题,只要日志文件超限,备份老的,创建新的即可
  • 然后实现自动删除超时的日志文件,每次启动日志系统的时候,以当前时间为基准,计算出1个月前的时间,遍历日志目录下的所有日志文件,因为日志文件都以时间命名,删除超过1个月的日志文件即可
  • 最后,我们只需要处理信息函数即可,捕获系统中的各种输出信息,输出到文件即可

关键代码

MyLog.h

  1. #ifndef MYLOG_H
  2. #define MYLOG_H
  3. #include <iostream>
  4. #include <QDateTime>
  5. #include <QMutexLocker>
  6. #include <QDir>
  7. #include <QTimer>
  8. #include <QTextStream>
  9. //最大保存文件大小
  10. const int g_logLimitSize = 5;
  11. class MyLog
  12. {
  13. public:
  14. MyLog();
  15. ~MyLog();
  16. static MyLog* getInstance();
  17. //消息处理函数
  18. static void messageHandler(QtMsgType type,
  19. const QMessageLogContext& context,
  20. const QString& msg);
  21. public:
  22. //打开并备份之前的日志文件
  23. void openAndBackupLogFile();
  24. void checkLogFiles();
  25. void autoDeleteLog();
  26. //安装消息处理函数
  27. void installMessageHandler();
  28. //卸载消息处理函数,并释放资源
  29. void uninstallMessageHandler();
  30. private:
  31. //日志文件夹目录
  32. QDir logDir;
  33. //重命名日志文件使用的定时器
  34. QTimer renameLogFileTimer;
  35. //刷新输出到日志文件的定时器
  36. QTimer flushLogFileTimer;
  37. //日志文件的创建时间
  38. QDate logFileCreateDate;
  39. //日志文件
  40. static QFile* logFile;
  41. //输出日志
  42. static QTextStream* logOut;
  43. //日志锁
  44. static QMutex logMutex;
  45. static QScopedPointer<MyLog> self;
  46. };
  47. #endif // MYLOG_H

MyLog.cpp

  1. #include "mylog.h"
  2. #include<QDebug>
  3. #include<QTextCodec>
  4. #define LOG 1
  5. //初始化静态变量
  6. QMutex MyLog::logMutex;
  7. QFile* MyLog::logFile = NULL;
  8. QTextStream* MyLog::logOut = NULL;
  9. QScopedPointer<MyLog> MyLog::self;
  10. //定义单例模式
  11. MyLog* MyLog::getInstance()
  12. {
  13. //还没有创建实例
  14. if(self.isNull())
  15. {
  16. //加把锁,只能有一个线程访问
  17. static QMutex mutex;
  18. //自动加解锁
  19. QMutexLocker locker(&mutex);
  20. //再次判断有没有实例,防止等待的时间中有线程获取到实例了
  21. if(self.isNull())
  22. {
  23. self.reset(new MyLog);
  24. }
  25. }
  26. return self.data();
  27. }
  28. MyLog::MyLog()
  29. {
  30. //设置日志文件夹的路径,./exe
  31. logDir.setPath("log");
  32. //获取日志的绝对路径
  33. QString logPath = logDir.absoluteFilePath("today.log");
  34. //获取日志文件创建的时间
  35. //保存日志文件最后的修改时间
  36. logFileCreateDate = QFileInfo(logPath).lastModified().date();
  37. //打开并备份日志文件
  38. openAndBackupLogFile();
  39. //每10分钟检查一次日志文件创建的时间
  40. renameLogFileTimer.setInterval(1000 * 60 *1000);
  41. renameLogFileTimer.start();
  42. //处理超时事件,10分钟重复一次
  43. QObject::connect(&renameLogFileTimer,&QTimer::timeout,[this](){
  44. QMutexLocker locker(&MyLog::logMutex);
  45. openAndBackupLogFile();
  46. checkLogFiles();
  47. autoDeleteLog();
  48. });
  49. //定时刷新日志输出到日志文件,1秒1刷新
  50. flushLogFileTimer.setInterval(1000);
  51. flushLogFileTimer.start();
  52. QObject::connect(&flushLogFileTimer,&QTimer::timeout,[](){
  53. #if LOG
  54. // 测试不停地写入当前时间到日志文件
  55. qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
  56. #endif
  57. //刷新
  58. QMutexLocker locker(&MyLog::logMutex);
  59. if(NULL != logOut)
  60. {
  61. logOut->flush();
  62. }
  63. });
  64. }
  65. MyLog::~MyLog()
  66. {
  67. if(NULL != logFile)
  68. {
  69. logFile->flush();
  70. logFile->close();
  71. logOut = NULL;
  72. logFile = NULL;
  73. }
  74. }
  75. //打开并备份之前的日志文件
  76. void MyLog::openAndBackupLogFile()
  77. {
  78. //有可能一天多次打开日志文件,使用追加的方式打开
  79. //目录不存在,创建目录
  80. if(!logDir.exists())
  81. {
  82. logDir.mkpath(".");
  83. }
  84. //log.txt的路径
  85. QString logPath = logDir.absoluteFilePath("today.log");
  86. //程序启动的时候,logfile为空
  87. if(logFile == NULL)
  88. {
  89. //创建新的
  90. logFile = new QFile(logPath);
  91. //只写,追加的方式打开日志文件
  92. //成功,创建文本流对象与日志文件关联,向日志文件写内容
  93. logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text |QIODevice::Append)) ? new QTextStream(logFile) : NULL;
  94. if(logOut != NULL)
  95. {
  96. //设置编码格式
  97. logOut->setCodec("UTF-8");
  98. }
  99. //日志文件第一次创建,创建日期无效,设置为修改日期
  100. if(logFileCreateDate.isNull())
  101. {
  102. logFileCreateDate = QDate::currentDate();
  103. }
  104. }
  105. //程序运行的时候,创建日期不是当前日期,更新日期,重命名,备份老的并生成新的log.txt
  106. if(logFileCreateDate != QDate::currentDate())
  107. {
  108. //先刷新缓冲区,确保内容先输出到文件里
  109. logFile->flush();
  110. logFile->close();
  111. //更新日期到备份文件
  112. QString backUpLogPath = logDir.absoluteFilePath(logFileCreateDate.toString("yyyy-MM-dd.log"));;
  113. //备份原来的日志
  114. QFile::copy(logPath,backUpLogPath);
  115. //删除原来的日志文件
  116. QFile::remove(logPath);
  117. //创建新的log.txt,进行更新
  118. //只写,截断的方式打开日志
  119. logFile = new QFile(logPath);
  120. logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) : NULL;
  121. //更新为修改时间
  122. logFileCreateDate = QDate::currentDate();
  123. if(logOut != NULL)
  124. {
  125. logOut->setCodec("UTF-8");
  126. }
  127. }
  128. }
  129. //检查文件大小
  130. void MyLog::checkLogFiles()
  131. {
  132. //日志文件大小超过5m,备份并重新创建日志文件
  133. if(logFile->size() > 1024* g_logLimitSize)
  134. {
  135. //清空缓冲
  136. logFile->flush();
  137. logFile->close();
  138. QString logPath = logDir.absoluteFilePath("today.log");
  139. //备份老的日志文件
  140. QString backUplogPath = logDir.absoluteFilePath(logFileCreateDate.toString("yyyy-MM-dd.log"));
  141. QFile::copy(logPath,backUplogPath);
  142. QFile::remove(logPath);
  143. //创建新的日志文件
  144. logFile = new QFile(logPath);
  145. logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) :NULL;
  146. logFileCreateDate = QDate::currentDate();
  147. if(logOut != NULL)
  148. {
  149. logOut->setCodec("UTF-8");
  150. }
  151. }
  152. }
  153. //自动删除超过时间的日志文件
  154. void MyLog::autoDeleteLog()
  155. {
  156. //当前时间
  157. QDateTime now = QDateTime::currentDateTime();
  158. //基准,30天前
  159. QDateTime dateTime1 = now.addDays(-30);
  160. QDateTime dateTime2;
  161. QString logPath = logDir.absoluteFilePath("today.log");
  162. //打开日志目录
  163. QDir dir(logPath);
  164. //获取目录下的所有文件信息列表
  165. QFileInfoList fileList = dir.entryInfoList();
  166. foreach(QFileInfo f, fileList)
  167. {
  168. //跳过文件名为空的文件
  169. if(f.baseName() == "")
  170. {
  171. continue;
  172. }
  173. //将文件名解析为日期对象
  174. dateTime2 = QDateTime::fromString(f.baseName(),"yyyy-MM-dd");
  175. //大于30天,删除
  176. if(dateTime2 < dateTime1)
  177. {
  178. dir.remove(f.absoluteFilePath());
  179. }
  180. }
  181. }
  182. //定义消息处理函数
  183. void MyLog::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
  184. {
  185. QMutexLocker locker(&MyLog::logMutex);
  186. QString level;
  187. switch (type) {
  188. case QtDebugMsg:
  189. level = "DEBUG";
  190. break;
  191. case QtInfoMsg:
  192. level = "INFO";
  193. break;
  194. case QtWarningMsg:
  195. level = "WARN";
  196. break;
  197. case QtCriticalMsg:
  198. level = "ERROR";
  199. break;
  200. case QtFatalMsg:
  201. level = "FATAL";
  202. break;
  203. default:
  204. break;
  205. }
  206. #if defined (Q_OS_WIN)
  207. QByteArray localMsg = QTextCodec::codecForName("GB2312")->fromUnicode(msg);
  208. #else
  209. QByteArray localMsg = msg.toLocal8Bit();
  210. #endif
  211. //输出到控制台
  212. std::cout << std::string(localMsg) << std::endl;
  213. if(NULL == MyLog::logOut)
  214. {
  215. return;
  216. }
  217. //输出到日志文件
  218. //获取文件名,去掉路径
  219. QString fileName = context.file;
  220. int index = fileName.lastIndexOf(QDir::separator());
  221. fileName = fileName.mid(index + 1);
  222. //写入日志信息
  223. (*MyLog::logOut) << QString("%1 - [%2] (%3:%4, %5): %6\n")
  224. .arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"))
  225. .arg(level)
  226. .arg(fileName)
  227. .arg(context.line)
  228. .arg(context.function)
  229. .arg(msg);
  230. }
  231. //安装
  232. void MyLog::installMessageHandler()
  233. {
  234. qInstallMessageHandler(MyLog::messageHandler);
  235. }
  236. //卸载
  237. void MyLog::uninstallMessageHandler()
  238. {
  239. qInstallMessageHandler(NULL);
  240. }

原文链接:https://www.cnblogs.com/kanhai1024/p/17897560.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号