用qt开发商业程序已经九年了,陆陆续续开发过至少几十个程序,除了一些算不算项目的小工具外,大部分的程序都需要有个日志的输出功能,希望可以将程序的运行状态存储到文本文件或者数据库或者做其他处理等,qt对这个日志输出也做了很好的封装,在Qt4是qInstallMsgHandler,Qt5里边是qInstallMessageHandler,有了这个神器,只要在你的项目中所有qdebug qinfo等输出的日志信息,都会重定向接收到,网上大部分人写的demo都是接收到输出打印日志存储到文本文件,其实这就带给很多人误解,容易产生以为日志只能输出到文本文件,其实安装了日志钩子以后,拿到了所有调试打印信息,你完全可以用来存储到数据库+html有颜色区分格式的文件+网络转发输出(尤其适用于嵌入式linux**面程序,现场不方便外接调试打印的设备)。
做过的这么多项目中,Qt4和Qt5的都有,我一般保留四个版本,4.8.7,为了兼容qt4, 5.7.0,最后的支持XP的版本, 最新的长期支持版本5.9.7 最高的新版本5.12。毫无疑问,我要封装的这个日志类,也要支持4+5的,而且提供友好的接口。
1:支持动态启动和停止。
2:支持日志存储的目录。
3:支持网络发出打印日志。
4:支持Qt4+Qt5。开箱即用。
5:支持多线程。
6:使用做到最简单,start即可。

完整代码下载:https://download.csdn.net/download/feiyangqingyun/11010379

网络接收日志工具截图

完整代码:

  1. #ifndef SAVELOG_H
  2. #define SAVELOG_H
  3.  
  4. #include <QObject>
  5.  
  6. class QFile;
  7. class QTcpSocket;
  8. class QTcpServer;
  9.  
  10. #ifdef quc
  11. #if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
  12. #include <QtDesigner/QDesignerExportWidget>
  13. #else
  14. #include <QtUiPlugin/QDesignerExportWidget>
  15. #endif
  16.  
  17. class QDESIGNER_WIDGET_EXPORT SaveLog : public QObject
  18. #else
  19. class SaveLog : public QObject
  20. #endif
  21.  
  22. {
  23. Q_OBJECT
  24. public:
  25. static SaveLog *Instance();
  26. explicit SaveLog(QObject *parent = );
  27. ~SaveLog();
  28.  
  29. private:
  30. static QScopedPointer<SaveLog> self;
  31.  
  32. //文件对象
  33. QFile *file;
  34. //是否重定向到网络
  35. bool toNet;
  36. //日志文件路径
  37. QString path;
  38. //日志文件名称
  39. QString name;
  40. //日志文件完整名称
  41. QString fileName;
  42.  
  43. signals:
  44. void send(const QString &content);
  45.  
  46. public slots:
  47. //启动日志服务
  48. void start();
  49. //暂停日志服务
  50. void stop();
  51. //保存日志
  52. void save(const QString &content);
  53.  
  54. //设置是否重定向到网络
  55. void setToNet(bool toNet);
  56. //设置日志文件存放路径
  57. void setPath(const QString &path);
  58. //设置日志文件名称
  59. void setName(const QString &name);
  60.  
  61. };
  62.  
  63. class SendLog : public QObject
  64. {
  65. Q_OBJECT
  66. public:
  67. static SendLog *Instance();
  68. explicit SendLog(QObject *parent = );
  69. ~SendLog();
  70.  
  71. private:
  72. static QScopedPointer<SendLog> self;
  73. QTcpSocket *socket;
  74. QTcpServer *server;
  75.  
  76. private slots:
  77. void newConnection();
  78.  
  79. public slots:
  80. //发送日志
  81. void send(const QString &content);
  82. };
  83.  
  84. #endif // SAVELOG_H
  85. #include "savelog.h"
  86. #include "qmutex.h"
  87. #include "qfile.h"
  88. #include "qtcpsocket.h"
  89. #include "qtcpserver.h"
  90. #include "qdatetime.h"
  91. #include "qapplication.h"
  92. #include "qtimer.h"
  93. #include "qstringlist.h"
  94.  
  95. #define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd"))
  96.  
  97. //日志重定向
  98. #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
  99. void Log(QtMsgType type, const char *msg)
  100. #else
  101. void Log(QtMsgType type, const QMessageLogContext &, const QString &msg)
  102. #endif
  103. {
  104. //加锁,防止多线程中qdebug太频繁导致崩溃
  105. QMutex mutex;
  106. QMutexLocker locker(&mutex);
  107. QString content;
  108.  
  109. //这里可以根据不同的类型加上不同的头部用于区分
  110. switch (type) {
  111. case QtDebugMsg:
  112. content = QString("%1").arg(msg);
  113. break;
  114.  
  115. case QtWarningMsg:
  116. content = QString("%1").arg(msg);
  117. break;
  118.  
  119. case QtCriticalMsg:
  120. content = QString("%1").arg(msg);
  121. break;
  122.  
  123. case QtFatalMsg:
  124. content = QString("%1").arg(msg);
  125. break;
  126. }
  127.  
  128. SaveLog::Instance()->save(content);
  129. }
  130.  
  131. QScopedPointer<SaveLog> SaveLog::self;
  132. SaveLog *SaveLog::Instance()
  133. {
  134. if (self.isNull()) {
  135. QMutex mutex;
  136. QMutexLocker locker(&mutex);
  137. if (self.isNull()) {
  138. self.reset(new SaveLog);
  139. }
  140. }
  141.  
  142. return self.data();
  143. }
  144.  
  145. SaveLog::SaveLog(QObject *parent) : QObject(parent)
  146. {
  147. //必须用信号槽形式,不然提示 QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
  148. //估计日志钩子可能单独开了线程
  149. connect(this, SIGNAL(send(QString)), SendLog::Instance(), SLOT(send(QString)));
  150.  
  151. file = new QFile(this);
  152. toNet = false;
  153. //默认取应用程序根目录
  154. path = qApp->applicationDirPath();
  155. //默认取应用程序可执行文件名称
  156. QString str = qApp->applicationFilePath();
  157. QStringList list = str.split("/");
  158. name = list.at(list.count() - ).split(".").at();
  159. fileName = "";
  160. }
  161.  
  162. SaveLog::~SaveLog()
  163. {
  164. file->close();
  165. }
  166.  
  167. //安装日志钩子,输出调试信息到文件,便于调试
  168. void SaveLog::start()
  169. {
  170. #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
  171. qInstallMsgHandler(Log);
  172. #else
  173. qInstallMessageHandler(Log);
  174. #endif
  175. }
  176.  
  177. //卸载日志钩子
  178. void SaveLog::stop()
  179. {
  180. #if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
  181. qInstallMsgHandler();
  182. #else
  183. qInstallMessageHandler();
  184. #endif
  185. }
  186.  
  187. void SaveLog::save(const QString &content)
  188. {
  189. //如果重定向输出到网络则通过网络发出去,否则输出到日志文件
  190. if (toNet) {
  191. emit send(content);
  192. } else {
  193. //方法改进:之前每次输出日志都打开文件,改成只有当日期改变时才新建和打开文件
  194. QString fileName = QString("%1/%2_log_%3.txt").arg(path).arg(name).arg(QDATE);
  195. if (this->fileName != fileName) {
  196. this->fileName = fileName;
  197. if (file->isOpen()) {
  198. file->close();
  199. }
  200.  
  201. file->setFileName(fileName);
  202. file->open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text);
  203. }
  204.  
  205. QTextStream logStream(file);
  206. logStream << content << "\n";
  207. }
  208. }
  209.  
  210. void SaveLog::setToNet(bool toNet)
  211. {
  212. this->toNet = toNet;
  213. }
  214.  
  215. void SaveLog::setPath(const QString &path)
  216. {
  217. this->path = path;
  218. }
  219.  
  220. void SaveLog::setName(const QString &name)
  221. {
  222. this->name = name;
  223. }
  224.  
  225. //网络发送日志数据类
  226. QScopedPointer<SendLog> SendLog::self;
  227. SendLog *SendLog::Instance()
  228. {
  229. if (self.isNull()) {
  230. QMutex mutex;
  231. QMutexLocker locker(&mutex);
  232. if (self.isNull()) {
  233. self.reset(new SendLog);
  234. }
  235. }
  236.  
  237. return self.data();
  238. }
  239.  
  240. SendLog::SendLog(QObject *parent)
  241. {
  242. socket = NULL;
  243. server = new QTcpServer(this);
  244. connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
  245.  
  246. int listenPort = ;
  247. #if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
  248. server->listen(QHostAddress::AnyIPv4, listenPort);
  249. #else
  250. server->listen(QHostAddress::Any, listenPort);
  251. #endif
  252. }
  253.  
  254. SendLog::~SendLog()
  255. {
  256. if (socket != NULL) {
  257. socket->disconnectFromHost();
  258. }
  259.  
  260. server->close();
  261. }
  262.  
  263. void SendLog::newConnection()
  264. {
  265. while (server->hasPendingConnections()) {
  266. socket = server->nextPendingConnection();
  267. }
  268. }
  269.  
  270. void SendLog::send(const QString &content)
  271. {
  272. if (socket != NULL && socket->isOpen()) {
  273. socket->write(content.toUtf8());
  274. socket->flush();
  275. }
  276. }

Qt编写调试日志输出类带网络转发(开源)的更多相关文章

  1. Android开发调试日志工具类[支持保存到SD卡]

    直接上代码: package com.example.callstatus; import java.io.File; import java.io.FileWriter; import java.i ...

  2. Qt编写图片及视频TCP/UDP网络传输

    一.前言 很多年前就做过类似的项目,无非就是将本地的图片上传到服务器,就这么简单,其实用http的post上传比较简单容易,无需自定义协议,直接设置好二进制数据即可,而采用TCP或者UDP通信的话,必 ...

  3. Qt编写的开源帖子集合(懒人专用)

    回顾自己学习Qt以来九年了,在这九年多时间里面,从本论坛学习不到不少的东西,今天特意整了一下自己开源过的资源的帖子,整理一起方便大家直接跳转下载,不统计不知道,一统计吓一跳,不知不觉开源了这么多代码, ...

  4. Qt编写自定义控件二动画按钮

    现在的web发展越来越快,很多流行的布局样式,都是从web开始的,写惯了Qt widgets 项目,很多时候想改进一下现有的人机交互,尤其是在现有的按钮上加一些动画的效果,例如鼠标移上去变大,移开还原 ...

  5. Qt编写自定义控件大全

    最新版可执行文件 http://pan.baidu.com/s/1i491FQP 不定期增加控件及修正BUG和改进算法. 总图: 1:动画按钮 * 1:可设置显示的图像和底部的文字 * 2:可设置普通 ...

  6. Qt编写自定义控件一开关按钮

    从2010年进入互联网+智能手机时代以来,各种各样的APP大行其道,手机上面的APP有很多流行的元素,开关按钮个人非常喜欢,手机QQ.360卫士.金山毒霸等,都有很多开关控制一些操作,在Qt widg ...

  7. 使用spring框架进行aop编程实现方法调用前日志输出

    aop编程 之使用spring框架实现方法调用前日志输出 使用spring框架实现AOP编程首先需要搭建spring框架环境: 使用Spring框架实现AOP工程编程之后,不需要我们去写代理工厂了,工 ...

  8. 日志输出--C#

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  9. Qt编写气体安全管理系统27-设备调试

    一.前言 设备调试核心就是将整个系统中的所有打印数据统一显示到一个模块上,一般都会将硬件通信的收发数据和对应的解析信号发出来或者qdebug出来,这个在调试阶段非常有用,可以具体追踪问题出在哪,哪个数 ...

随机推荐

  1. [Asp.net core]bootstrap分页

    摘要 一直在用前后端分离,在一个后台管理的页面中,尝试封装了一个辅助类. 类 /// <summary> /// 分页viewmodel /// </summary> /// ...

  2. Use Multiple log4net Outputs from One Application

    Introduction This is an article simply to demonstrate how to use several output log files depending ...

  3. windows多线程同步--事件

    推荐参考博客:秒杀多线程第六篇 经典线程同步 事件Event   事件是内核对象,多用于线程间通信,可以跨进程同步 事件主要用到三个函数:CreateEvent,OpenEvent,SetEvent, ...

  4. 使用Buildozer部署Kivy到移动设备上

    在安装好Buildozer软件之后,我们在包含main.py的文件夹下运行buildozer init这个命令,然后我们就会看到在该文件夹下有一个buildozer.spec这个文件,这个文件主要是用 ...

  5. Selenium 致命杀手(有关自动化的通病)

    Do your scripts suffer from the following automation test flaky symptoms? Test randomly fail Works o ...

  6. C语言100个经典的算法

    C语言的学习要从基础開始.这里是100个经典的算法-1C语言的学习要从基础開始,这里是100个经典的算法 题目:古典问题:有一对兔子,从出生后第3个月起每一个月都生一对兔子.小兔 子长到第三个月后每一 ...

  7. 【Linux】linux/unix下telnet提示Escape character is '^]'的意义

    在linux/unix下使用telnet hostname port连接上主机后会提示Escape character is '^]' 这个提示的意思是按Ctrl + ] 会呼出telnet的命令行, ...

  8. Ubuntu 13.10 录音有特别大噪音解决办法

    现在物理机跑Ubuntu,平常的QQ只能在虚拟机里跑了:起初和别人QQ语音,别人能听到很大的噪音,以为是虚拟机的回音,属于正常现象,结果我用虚拟机里边的录音工具和Ubuntu里的录音工具测试一下,发现 ...

  9. SNF开发平台WinForm-Grid表格控件大全

    我们在开发系统时,会有很多种控件进行展示,甚至有一些为了方便的一些特殊需求. 那么下面就介绍一些我们在表格控件里常用的方便的控件:   1.Grid表格查询条 Grid表格下拉 3.Grid表格弹框选 ...

  10. git代码统计

    1.统计一段时间的代码量 git log --format='%aN' | sort -u | while read name; do echo -en "$name\t"; gi ...