• 1208阅读
  • 11回复

Qt编写调试日志输出类带网络转发(开源) [复制链接]

上一主题 下一主题
在线liudianwu
 

用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即可。
完整代码下载: savelog.zip (6 K) 下载次数:168
网络转发效果图:


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



专业各种自定义控件编写+UI定制+输入法定制+视频监控+工业控制+仪器仪表+嵌入式linux+各种串口网络通信,童叟无欺,量大从优,欢迎咨询购买定制!你正好需要,我正好专业!QQ:517216493 微信:feiyangqingyun Email:feiyangqingyun@163.com
群号:853086607(Qt交流大会,雨田哥群,不定期上传作品,解答作品中相关问题!) 312125701(QtQML多多指教群) 46679801(Qt开发技术交流群-5000人大群)
离线aiwen.zhang

只看该作者 1楼 发表于: 03-10
太强了。
离线jokerhuang

只看该作者 2楼 发表于: 03-11
我想说,真的开心死我了!从楼主的分享代码里学到了好多编程知识!
离线九重水

只看该作者 3楼 发表于: 03-11
九年?
哪年毕业的?
离线richards

只看该作者 4楼 发表于: 03-12
这个不错。我用过最好用的 就是 android的 monitor logcat
离线ccazqyy

只看该作者 5楼 发表于: 03-12
          
离线liuchangyin

只看该作者 6楼 发表于: 03-12
很实用
离线kaikai_king

只看该作者 7楼 发表于: 03-14
大佬  带带我
离线vaehate

只看该作者 8楼 发表于: 03-25
终于等到大佬发帖了
离线mengjin

只看该作者 9楼 发表于: 03-26
学习了
离线liuyuanan

只看该作者 10楼 发表于: 04-24
我们现在使用的日志库是qt4log开源库,唯一的遗憾是不能通过网络转发
离线greensky10

只看该作者 11楼 发表于: 05-14
感谢刘大佬分享!!!
快速回复
限100 字节
 
上一个 下一个