用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

网络接收日志工具截图

完整代码:

#ifndef SAVELOG_H
#define SAVELOG_H #include <QObject> class QFile;
class QTcpSocket;
class QTcpServer; #ifdef quc
#if (QT_VERSION < QT_VERSION_CHECK(5,7,0))
#include <QtDesigner/QDesignerExportWidget>
#else
#include <QtUiPlugin/QDesignerExportWidget>
#endif class QDESIGNER_WIDGET_EXPORT SaveLog : public QObject
#else
class SaveLog : public QObject
#endif {
Q_OBJECT
public:
static SaveLog *Instance();
explicit SaveLog(QObject *parent = );
~SaveLog(); private:
static QScopedPointer<SaveLog> self; //文件对象
QFile *file;
//是否重定向到网络
bool toNet;
//日志文件路径
QString path;
//日志文件名称
QString name;
//日志文件完整名称
QString fileName; signals:
void send(const QString &content); public slots:
//启动日志服务
void start();
//暂停日志服务
void stop();
//保存日志
void save(const QString &content); //设置是否重定向到网络
void setToNet(bool toNet);
//设置日志文件存放路径
void setPath(const QString &path);
//设置日志文件名称
void setName(const QString &name); }; class SendLog : public QObject
{
Q_OBJECT
public:
static SendLog *Instance();
explicit SendLog(QObject *parent = );
~SendLog(); private:
static QScopedPointer<SendLog> self;
QTcpSocket *socket;
QTcpServer *server; private slots:
void newConnection(); public slots:
//发送日志
void send(const QString &content);
}; #endif // SAVELOG_H
#include "savelog.h"
#include "qmutex.h"
#include "qfile.h"
#include "qtcpsocket.h"
#include "qtcpserver.h"
#include "qdatetime.h"
#include "qapplication.h"
#include "qtimer.h"
#include "qstringlist.h" #define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd")) //日志重定向
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
void Log(QtMsgType type, const char *msg)
#else
void Log(QtMsgType type, const QMessageLogContext &, const QString &msg)
#endif
{
//加锁,防止多线程中qdebug太频繁导致崩溃
QMutex mutex;
QMutexLocker locker(&mutex);
QString content; //这里可以根据不同的类型加上不同的头部用于区分
switch (type) {
case QtDebugMsg:
content = QString("%1").arg(msg);
break; case QtWarningMsg:
content = QString("%1").arg(msg);
break; case QtCriticalMsg:
content = QString("%1").arg(msg);
break; case QtFatalMsg:
content = QString("%1").arg(msg);
break;
} SaveLog::Instance()->save(content);
} QScopedPointer<SaveLog> SaveLog::self;
SaveLog *SaveLog::Instance()
{
if (self.isNull()) {
QMutex mutex;
QMutexLocker locker(&mutex);
if (self.isNull()) {
self.reset(new SaveLog);
}
} return self.data();
} SaveLog::SaveLog(QObject *parent) : QObject(parent)
{
//必须用信号槽形式,不然提示 QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
//估计日志钩子可能单独开了线程
connect(this, SIGNAL(send(QString)), SendLog::Instance(), SLOT(send(QString))); file = new QFile(this);
toNet = false;
//默认取应用程序根目录
path = qApp->applicationDirPath();
//默认取应用程序可执行文件名称
QString str = qApp->applicationFilePath();
QStringList list = str.split("/");
name = list.at(list.count() - ).split(".").at();
fileName = "";
} SaveLog::~SaveLog()
{
file->close();
} //安装日志钩子,输出调试信息到文件,便于调试
void SaveLog::start()
{
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
qInstallMsgHandler(Log);
#else
qInstallMessageHandler(Log);
#endif
} //卸载日志钩子
void SaveLog::stop()
{
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0))
qInstallMsgHandler();
#else
qInstallMessageHandler();
#endif
} void SaveLog::save(const QString &content)
{
//如果重定向输出到网络则通过网络发出去,否则输出到日志文件
if (toNet) {
emit send(content);
} else {
//方法改进:之前每次输出日志都打开文件,改成只有当日期改变时才新建和打开文件
QString fileName = QString("%1/%2_log_%3.txt").arg(path).arg(name).arg(QDATE);
if (this->fileName != fileName) {
this->fileName = fileName;
if (file->isOpen()) {
file->close();
} file->setFileName(fileName);
file->open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text);
} QTextStream logStream(file);
logStream << content << "\n";
}
} void SaveLog::setToNet(bool toNet)
{
this->toNet = toNet;
} void SaveLog::setPath(const QString &path)
{
this->path = path;
} void SaveLog::setName(const QString &name)
{
this->name = name;
} //网络发送日志数据类
QScopedPointer<SendLog> SendLog::self;
SendLog *SendLog::Instance()
{
if (self.isNull()) {
QMutex mutex;
QMutexLocker locker(&mutex);
if (self.isNull()) {
self.reset(new SendLog);
}
} return self.data();
} SendLog::SendLog(QObject *parent)
{
socket = NULL;
server = new QTcpServer(this);
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection())); int listenPort = ;
#if (QT_VERSION > QT_VERSION_CHECK(5,0,0))
server->listen(QHostAddress::AnyIPv4, listenPort);
#else
server->listen(QHostAddress::Any, listenPort);
#endif
} SendLog::~SendLog()
{
if (socket != NULL) {
socket->disconnectFromHost();
} server->close();
} void SendLog::newConnection()
{
while (server->hasPendingConnections()) {
socket = server->nextPendingConnection();
}
} void SendLog::send(const QString &content)
{
if (socket != NULL && socket->isOpen()) {
socket->write(content.toUtf8());
socket->flush();
}
}

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. vim 语法着色完全配置

    原文地址:http://blog.sina.com.cn/s/blog_878940b3010156ku.html 在终端输入:sudo vim /etc/vim/vimrc   打开配置文件.编辑命 ...

  2. VS2010链接TFS遇见错误:TF204017,没有访问工作区域,需要一个或者多个必须权限

    最近刚刚搭建好服务器,然后准备将VSS源代码迁移到TFS源代码管理服务器上面.在我本机先用的服务器帐号来上传初始化源代码数据库,然后我又用自己的帐号进行迁出代码的时候发生的异常. 造成上述错误,主要是 ...

  3. Linux 保护文件 不给修改

    chatter +i  file 文件不能删除,不能更改,不能移动 chatter -i  file  恢复 lsattr file 查看 ----i--------e-- file 修改会提示: f ...

  4. 微软BI 之SSIS 系列 - 通过 ROW_NUMBER 或 Script Component 为数据流输出添加行号的方法

    开篇介绍 上午在天善回答看到这个问题 - SSIS 导出数据文件,能否在第一列增加一个行号,很快就帮助解决了,方法就是在 SQL 查询的时候加一个 ROW_NUMBER() 就可以了. 后来想起在两年 ...

  5. JAVA代码中获取JVM信息

    一.JAVA中获取JVM的信息 原理,利用JavaSDK自带的ManagementFactory类来获取. 二.获取信息 1.获取进程ID @Test public void test1() { Ru ...

  6. linux nfs共享文件

    linux文件共享可以有多种方式:samba,nfs,ftp等等 nfs在linux之间效率高些: function nfs(){ share_folder="/data1 192.168. ...

  7. openssl - 数字证书的编程解析

    原文链接: http://www.cangfengzhe.com/wangluoanquan/37.html 这篇文章主要介绍PKI公钥体系中非常核心元素——数字证书的编程解析.在SSL,SET等安全 ...

  8. Apache Spark 2.2.0 新特性详细介绍

    本章内容: 待整理 参考文献: Apache Spark 2.2.0新特性详细介绍 Introducing Apache Spark 2.2

  9. Django Mysql SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED

    Django 执行makemigrations  的时候报错: django.db.utils.ProgrammingError: (1064, "You have an error in ...

  10. 使用Docker-Docker for Web Developers(2)

    1. 使用镜像 1.1 在Docker Hub上查找镜像 我们查找一下之前博客里面,推送到Docker Hub里面的bage88/docker-demo,能看到有2个仓库,第一个就是我们上次上传的镜像 ...