当前位置: 首页 > news >正文

建设一个网站需要做哪些事情/缅甸新闻最新消息

建设一个网站需要做哪些事情,缅甸新闻最新消息,电脑网站做名片,企业网站建设宣贯Qt重定向QDebug,自定义一个简易的日志管理类 Chapter1 Qt重定向QDebug,自定义一个简易的日志管理类0.前言1.最简单的操作运行结果2.实现一个简易的日志管理类 Chapter2 Qt::Qt Log日志模块Qt Log日志模块官方解释官方Demo思路 Chapter3 QT日志模块的个性…

Qt重定向QDebug,自定义一个简易的日志管理类

  • Chapter1 Qt重定向QDebug,自定义一个简易的日志管理类
    • 0.前言
    • 1.最简单的操作
    • 运行结果
    • 2.实现一个简易的日志管理类
  • Chapter2 Qt::Qt Log日志模块
    • Qt Log日志模块
    • 官方解释
    • 官方Demo
    • 思路
  • Chapter3 QT日志模块的个性化使用
    • 格式化日志输出
    • 输出日志到文本
    • 日志输出对象信息
  • Chapter4 Qt 自定义日志类($$$)
  • Chapter5 简单易用的Qt日志模块($$$)
    • 引言
    • 一、日志实现方法
      • 代码实现
    • 二、崩溃处理
      • 代码实现
    • 小结
  • Chapter6 Qt/C++开源作品39-日志输出增强版V2022($$$)
    • 一、前言
    • 二、主要功能
    • 三、效果图
    • 四、开源主页
    • 五、核心代码


Chapter1 Qt重定向QDebug,自定义一个简易的日志管理类

原文链接:https://blog.csdn.net/gongjianbo1992/article/details/108030391

0.前言

相对于第三方的日志库,在 Qt 中使用 QDebug 打印更便捷,有时候也需要对 QDebug 输出进行重定向,如写入文件等。

在 Qt4 中使用 qInstallMsgHandler 函数设置重定向的函数指针:

typedef void (*QtMsgHandler)(QtMsgType, const char *);
Q_CORE_EXPORT QT_DEPRECATED QtMsgHandler qInstallMsgHandler(QtMsgHandler);

在 Qt5 中应该使用 qInstallMessageHandler 来注册函数指针:

typedef void (*QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &);
Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);

返回的函数指针我们可以保存起来,需要输出到控制台时进行调用。

默认 Release 模式 QMessageLogContext 不含上下文信息,可以用宏定义 QT_MESSAGELOGCONTEXT 开启,pro 文件加上:

DEFINES += QT_MESSAGELOGCONTEXT

1.最简单的操作

一个最简单的示例如下,重定向到文件:

#include <QApplication>
#include <QMutex>
#include <QMutexLocker>
#include <QFile>
#include <QTextStream>
#include <QDebug>//重定向qdebug输出到文件
void myMessageHandle(QtMsgType , const QMessageLogContext& , const QString& msg)
{static QMutex mut; //多线程打印时需要加锁QMutexLocker locker(&mut);QFile file("log.txt");if(file.open(QIODevice::WriteOnly|QIODevice::Append)){QTextStream stream(&file);stream<<msg<<endl;file.close();}
}int main()
{//设置重定向操作的函数qInstallMessageHandler(myMessageHandle);qDebug()<<"Test debug111";qDebug()<<"Test debug222";return 0;
}

运行结果

在这里插入图片描述

2.实现一个简易的日志管理类

需求很简单,同时输出到界面中的编辑框、文件、控制台。

代码链接:https://github.com/gongjianbo/SimpleQtLogger

运行效果(图片为旧版截图):
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
输出到控制台,我是保存了调用 qInstallMessageHandler 时返回的函数指针,然后调用进行默认的输出。

输出到文件,因为函数调用发生在 qDebug() 调用的线程,所以需要加锁。

输出到界面,我使用了信号槽的方式,将文本发送给 connect 的槽。

此外,增加了按日期和文件大小来新建文件的逻辑。

当然,还可以添加一些细节,如日志等级的控制等。

下面是部分源码:

#include <QApplication>#include "LogManager.h"
#include "mainwindow.h"int main(int argc, char *argv[])
{LogManager::getInstance()->initManager();//初始化QApplication a(argc, argv);MainWindow w;w.show();return a.exec();
}
#pragma once
#include <QObject>
#include <QFile>
#include <QMutex>
#include <QDebug>/*** @brief 简易的日志管理类,作为单例* @author 龚建波 - https://github.com/gongjianbo* @date 2020-08-13* @details* 1.初始化时调用 initManager 重定向 QDebug 输出* 析构时自动调用 freeManager,也可以手动调用 freeManager* 2.根据时间戳每天重新生成一个文件,超过文件大小也会重新生成*/
class LogManager : public QObject
{Q_OBJECTQ_DISABLE_COPY_MOVE(LogManager)LogManager();
public:~LogManager();// 获取单例实例static LogManager *getInstance();// 获取带 html 样式标签的富文本Q_INVOKABLE static QString richText(int msgType, const QString &log);// 初始化,如重定向等void initManager(const QString &dir = QString());// 释放void freeManager();// 文件最大大小,超过则新建文件,单位字节qint64 getFileSizeLimit() const;void setFileSizeLimit(qint64 limit);private:// 重定向到此接口static void outputHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);// 获取重定向的打印信息,在静态函数种回调该接口void outputLog(QtMsgType type, const QMessageLogContext &context, const QString &msg);// 计算下一次生成文件的时间qint64 calcNextTime() const;// 每次写入时判断是否打开,是否需要新建文件void prepareFile();signals:// 可以关联信号接收日志信息,如显示到 ui 中// 注意,如果槽函数为 lambda 或者其他没有接收者的情况,需要保证槽函数中的变量有效性// 因为 static 变量的生命周期更长,可能槽函数所在模块已经释放资源,最好 connect 加上接收者void newLog(int msgType, const QString &log);private:// 保留默认 handle,用于输出到控制台QtMessageHandler defaultOutput = nullptr;// 输出到文件QFile logFile;// 输出路径QString logDir;// 多线程操作时需要加锁mutable QMutex logMutex;// 下一次生成文件的时间戳,单位毫秒qint64 fileNextTime{ 0 };// 文件最大大小,超过则新建文件,单位字节qint64 fileSizeLimit{ 1024 * 1024 * 32 };
};
#include "LogManager.h"
#include <QCoreApplication>
#include <QDir>
#include <QThread>
#include <QTextStream>
#include <QDateTime>LogManager::LogManager()
{}LogManager::~LogManager()
{freeManager();
}LogManager *LogManager::getInstance()
{// 单例,初次调用时实例化static LogManager instance;return &instance;
}QString LogManager::richText(int msgType, const QString &log)
{QString log_text;QTextStream stream(&log_text);switch (msgType) {case QtDebugMsg: stream << "<span style='color:green;'>"; break;case QtInfoMsg: stream << "<span style='color:blue;'>"; break;case QtWarningMsg: stream << "<span style='color:gold;'>"; break;case QtCriticalMsg: stream << "<span style='color:red;'>"; break;case QtFatalMsg: stream << "<span style='color:red;'>"; break;default: stream << "<span style='color:red;'>"; break;}stream << log << "</span>";return log_text;
}void LogManager::initManager(const QString &dir)
{QMutexLocker locker(&logMutex);// 保存路径logDir = dir;if (logDir.isEmpty()){// 用到了 QCoreApplication::applicationDirPath(),需要先实例化一个appif (qApp) {logDir = qApp->applicationDirPath() + "/log";} else {int argc = 0;QCoreApplication app(argc,nullptr);logDir = app.applicationDirPath() + "/log";}}// 计算下次创建文件的时间点fileNextTime = calcNextTime();// 重定向qdebug到自定义函数defaultOutput = qInstallMessageHandler(LogManager::outputHandler);
}void LogManager::freeManager()
{QMutexLocker locker(&logMutex);logFile.close();if (defaultOutput) {qInstallMessageHandler(defaultOutput);defaultOutput = nullptr;}
}qint64 LogManager::getFileSizeLimit() const
{return fileSizeLimit;
}void LogManager::setFileSizeLimit(qint64 limit)
{fileSizeLimit = limit;
}void LogManager::outputHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{// 转发给单例的成员函数LogManager::getInstance()->outputLog(type, context, msg);
}void LogManager::outputLog(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{// widget 中的 log,context.category = default// qml 中的 log,context.category = qml,此时默认的 output 会增加一个 "qml:" 前缀输出// fprintf(stderr, "print: type = %d, category = %s \n", type, context.category);// 如果要写文件需要加锁,因为函数调用在 debug 调用线程QMutexLocker locker(&logMutex);QString out_text;QTextStream stream(&out_text);// 时间stream << QDateTime::currentDateTime().toString("[yyyy-MM-dd hh:mm:ss]");// 日志类型switch (type) {case QtDebugMsg: stream << "[Debug]"; break;case QtInfoMsg: stream << "[Info]"; break;case QtWarningMsg: stream << "[Warning]"; break;case QtCriticalMsg: stream << "[Critical]"; break;case QtFatalMsg: stream << "[Fatal]"; break;default: stream << "[Unknown]"; break;}// 线程 idstream << "[" << QThread::currentThreadId() << "]";// 输出位置stream << "[" << context.file << ":" << context.line << "]";// 日志信息stream << msg;// 判断是否需要打开或者新建文件prepareFile();if (logFile.isOpen()) {// 写入文件stream.setDevice(&logFile);stream << out_text << Qt::endl;}// 发送信号给需要的对象,如 ui 上显示日志emit newLog(type, out_text);// 默认的输出,控制台// 区分日志类型给文本加颜色// 常见格式为:\e[显示方式;背景颜色;前景文字颜色m << 输出字符串 << \e[0m// 其中 \e=\033// -----------------// 背景色  字体色// 40:    30:    黑// 41:    31:    红// 42:    32:    绿// 43:    33:    黄// 44:    34:    蓝// 45:    35:    紫// 46:    36:    深绿// 47:    37:    白// -----------------QString cmd_text;stream.setString(&cmd_text);switch (type) {case QtDebugMsg: // debug 绿色stream << "\033[32m"; break;case QtInfoMsg: // info 蓝色stream << "\033[34m"; break;case QtWarningMsg: // warning 黄色stream << "\033[33m"; break;case QtCriticalMsg: // critical 红字stream << "\033[31m"; break;case QtFatalMsg: // fatal 黑底红字// qFatal 表示致命错误,默认处理会报异常的stream << "\033[0;31;40m"; break;default: // defualt 默认颜色stream << "\033[0m"; break;}stream << out_text << "\033[0m";defaultOutput(type, context, cmd_text);
}qint64 LogManager::calcNextTime() const
{// 可以参考 spdlog 的 daily_file_sink 优化,这里先用 Qt 接口进行实现return QDate::currentDate().addDays(1).startOfDay().toMSecsSinceEpoch();
}void LogManager::prepareFile()
{// 写入文件// 先计算好下一次生成文件的时间点,然后和当前进行比较,这里没有考虑调节系统日期的情况if (fileNextTime <= QDateTime::currentDateTime().toMSecsSinceEpoch()){logFile.close();// 计算下次创建文件的时间点fileNextTime = calcNextTime();}// 文件超过了大小if (logFile.isOpen() && logFile.size() >= fileSizeLimit) {logFile.close();}// 生成文件名,打开文件if (!logFile.isOpen()) {// 创建文件前创建目录,QFile 不会自动创建不存在的目录QDir dir(logDir);if (!dir.exists()) {dir.mkpath(logDir);}// 文件日期QString file_day = QDate::currentDate().toString("yyyyMMdd");QString file_path = QString("%1/log_%2.txt").arg(logDir).arg(file_day);logFile.setFileName(file_path);if (logFile.exists() && logFile.size() >= fileSizeLimit) {QString file_time = QTime::currentTime().toString("hhmmss");file_path = QString("%1/log_%2_%3.txt").arg(logDir).arg(file_day).arg(file_time);logFile.setFileName(file_path);}// 打开新的文件// Append 追加模式,避免同一文件被清除if (!logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {emit newLog(QtWarningMsg, "Open log file error:" + logFile.errorString() + logFile.fileName());}}
}

Chapter2 Qt::Qt Log日志模块

原文链接:https://blog.csdn.net/u011218356/article/details/103344231

Qt Log日志模块

简介
这几天在交接工作,把之前手头的一个项目交接一下,想着增加一个日志模块,去查了一下,Qt自带的日志模块 qInstallMessageHandler 。

qInstallMessageHandler–说明
qInstallMessageHandler 位于 - Global Qt Declarations下边,属于全局函数。

官方解释

QtMessageHandler qInstallMessageHandler(QtMessageHandler handler)

Installs a Qt message handler which has been defined previously. Returns a pointer to the previous message handler.
The message handler is a function that prints out debug messages, warnings, critical and fatal error messages. The Qt library (debug mode) contains hundreds of warning messages that are printed when internal errors (usually invalid function arguments) occur. Qt built in release mode also contains such warnings unless QT_NO_WARNING_OUTPUT and/or QT_NO_DEBUG_OUTPUT have been set during compilation. If you implement your own message handler, you get total control of these messages.
The default message handler prints the message to the standard output under X11 or to the debugger under Windows. If it is a fatal message, the application aborts immediately.
Only one message handler can be defined, since this is usually done on an application-wide basis to control debug output.
To restore the message handler, call qInstallMessageHandler(0).

官方解释主要说了 qInstallMesageHandler 的作用,其实主要是是对 打印消息的控制,能控制打印的输出,调试,信息,警告,严重,致命错误等五个等级。

官方Demo

  #include <qapplication.h>#include <stdio.h>#include <stdlib.h>void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg){QByteArray localMsg = msg.toLocal8Bit();switch (type) {case QtDebugMsg:fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);break;case QtInfoMsg:fprintf(stderr, "Info: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);break;case QtWarningMsg:fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);break;case QtCriticalMsg:fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);break;case QtFatalMsg:fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function);abort();}}int main(int argc, char **argv){qInstallMessageHandler(myMessageOutput);QApplication app(argc, argv);...return app.exec();}

源码

思路

日志模块思路如下:
1.读取日志配置文件,设置文件输出等级。可以用做,在正式项目中调试与日常关键信息打印。
2.Log消息输出方法–输出文本。
3.消息模块注册(个人理解)

源码
枚举变量–设置日志等级 0~4 5个等级

enum{Fatal = 0,Critical,Warning,Info,Debug
}LogLeaver;
int LogType = 4; //日志等级初始化设置
//初始化读取配置文件
void ReadLogInit()
{QFile file ("./log_conf.ini");if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){//判断文件是否可执行return;}while (!file.atEnd()) {QByteArray  strBuf = file.readLine();if(strBuf == "[LOG_CONFIG]\n"){strBuf = file.readLine();LogType = strBuf.mid(strBuf.size()-1,1).toInt();}}
}
//配置文件格式
[LOG_CONFIG]
LogLeaver=4
void message_output(QtMsgType type,const QMessageLogContext &context,const QString &msg)
{//加锁:避免对文件的同时读写static QMutex mutex;mutex.lock();//读写消息QByteArray localMsg = msg.toLocal8Bit();//输出的字符串QString strOutStream = "";//case 生成要求格式日志文件,加日志等级过滤switch (type) {case QtDebugMsg:if(LogType == Debug){strOutStream = QString("%1 %2 %3 %4 [Debug] %5 \n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(QString(context.file)).arg(context.line).arg(QString(context.function)).arg(QString(localMsg));}break;case QtInfoMsg:if(LogType >= Info){strOutStream = QString("%1 %2 %3 %4 [Info]: %5 \n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(QString(context.file)).arg(context.line).arg(QString(context.function)).arg(QString(localMsg));}break;case QtWarningMsg:if(LogType >= Warning){strOutStream = QString("%1 %2 %3 %4 [Warning]: %5 \n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(QString(context.file)).arg(context.line).arg(QString(context.function)).arg(QString(localMsg));}break;case QtCriticalMsg:if(LogType >= Critical){strOutStream = QString("%1 %2 %3 %4 [Critical]: %5 \n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(QString(context.file)).arg(context.line).arg(QString(context.function)).arg(QString(localMsg));}break;case QtFatalMsg:if(LogType >= Fatal){strOutStream = QString("%1 %2 %3 %4 [Fatal]: %5 \n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(QString(context.file)).arg(context.line).arg(QString(context.function)).arg(QString(localMsg));}abort();}//每天生成一个新的log日志文件,文件名 yyyyMMdd.txtQString strFileName = QString("%1.txt").arg(QDateTime::currentDateTime().date().toString("yyyyMMdd"));QFile logfile(strFileName);logfile.open(QIODevice::WriteOnly | QIODevice::Append);if(strOutStream != ""){QTextStream logStream(&logfile);logStream<<strOutStream<<"\r\n";}//清楚缓存文件,解锁logfile.flush();logfile.close();mutex.unlock();
}
int main(int argc, char *argv[])
{ReadLogInit();//读取日志等级qInstallMessageHandler(message_output);//安装消息处理函数,依靠回调函数,重定向,全局处理QApplication a(argc, argv);qInfo()<<"\r\n\r\n\r\n**PCCamera start**";//to doing......
}

Chapter3 QT日志模块的个性化使用

原文链接:https://blog.csdn.net/yang1fei2/article/details/125210633

在开发QT程序的时候,很多开发者也就仅仅用QT的日志模块qDebug一下调试信息,在真正的日志记录上还是采用一些别的日志库。其实QT的日志模块还是很强大的,可以满足日常的基本需求。这里就详细介绍一下QT日志模块的个性化使用方法。

格式化日志输出

默认情况下,日志格式是只输出对应的日志内容没有额外信息的。我们可以通过修改环境变量QT_MESSAGE_PATTERN或者调用方法 qSetMessagePattern来修改日志的输出格式。日志格式中常用的占位符号如下所示:

%{appname}     应用程序的名称(QCoreApplication::applicationName())
%{category}    日志所处的领域
%{file}        打印该日志的文件路径 
%{function}    打印日志的函数
%{line}        打印日志在文件中的行数
%{message}     日志的内容
%{pid}         打印日志的程序的PID(QCoreApplication::applicationPid())
%{threadid}    打印日志的线程ID
%{qthreadptr}  打印日志的线程指针
%{type}        日志级别("debug", "warning", "critical" or "fatal")
%{time process}日志发生时程序启动了多久
%{time boot}   日志发生时系统启动了多久
%{time [format]}以固定时间格式输出日志打印的时间,默认为QISODate格式

格式化日志的调用方法如下:

int main(int argc,char*argv[])
{QCoreApplication app(argc, argv);qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss}--[%{type}]--%{function}:%{message}");qDebug() << "exception occured";qInfo() <<  "call other function";return app.exec();
}

输出的日志内容格式如下:

2022-06-09 10:09:54--[debug]--main:exception occured
2022-06-09 10:09:55--[info]--main:call other function

我们还可以使用条件变量

%{if-debug}, %{if-info} %{if-warning}, %{if-critical} or %{if-fatal}

给不同级别的日志指定不同的格式,使用方法如下:

int main(int argc,char*argv[])
{QCoreApplication app(argc, argv);//针对Warning信息和Fatal信息输出了额外的内容qputenv("QT_MESSAGE_PATTERN", QByteArray("%{time yyyy-MM-dd hh:mm:ss} [%{type}]%{if-warning}[%{function}]%{endif}%{if-fatal}[%{function}--%{line}]%{endif}:%{message}"));qDebug() << "debuginfo occured";qInfo() <<  "call other function";qWarning() << "doesn't work";qFatal("fatal error");return app.exec();
}

输出的日内容如下:

2022-06-09 10:48:32 [debug]:debuginfo occured
2022-06-09 10:48:32 [info]:call other function
2022-06-09 10:48:32 [warning][main]:doesn't work
2022-06-09 10:48:32 [fatal][main--116]:fatal error

我们可以通过修改QT_MESSAGE_PATTERN环境变量动态的修改日志的输出格式。如果同时调用qSetMessagePattern和QT_MESSAGE_PATTERN环境变量来修改日志的输出格式,那么QT_MESSAGE_PATTERN的优先级要高于qSetMessagePattern。

输出日志到文本

QT默认的日志内容是输出到终端的,不会输出到文件里面,如果需要将日志内容输出到文件中我们需要通过qInstallMessageHandler设置日志信息处理函数。使用方法如下:

//日志消息的处理函数
void logmessageHander(QtMsgType type, const QMessageLogContext& context, const QString& message)
{//获取格式化的日志信息QString typeStr = qFormatLogMessage(type,context,message);//可以根据日志的级别进行过滤QString levelText;switch (type) {case QtDebugMsg:levelText = "Debug";break;case QtInfoMsg:levelText = "Info";break;case QtWarningMsg:levelText = "Warning";break;case QtCriticalMsg:levelText = "Critical";break;case QtFatalMsg:levelText = "Fatal";break;}QFile file("myapp.log");file.open(QIODevice::WriteOnly | QIODevice::Append);QTextStream textStream(&file);textStream << typeStr << endl;
}
int main(int argc,char*argv[])
{QCoreApplication app(argc, argv);qSetMessagePattern("%{time yyyy-MM-dd hh:mm:ss} [%{type}]%{if-warning}[%{function}]%{endif}%{if-fatal}[%{function}--%{line}]%{endif}:%{message}");qInstallMessageHandler(logmessageHander);qDebug() << "debuginfo occured";qInfo() <<  "call other function";qWarning() << "doesn't work";qFatal("fatal error");return app.exec();
}

如果需要关闭日志输出,取消之前注册的日志处理函数,我们可以调用

//取消注册的日志处理函数
qInstallMessageHandler(0);

日志输出对象信息

在调试一些复杂对象的时候,我们需要输出对象的成员信息到日志当中。但是默认情况下qt的日志库是不支持输出自定义对象的。这时候我们可以通过重写操作符实现对自定义象的日志输出。使用方法如下:

//消费者信息类
struct Customer {QString name;int age;QString clientid;QString addr;
};QDebug operator<<(QDebug debug, const Customer& customer)
{//保存QDebug的状态QDebugStateSaver saver(debug);debug.nospace() << "("<< "name: " << customer.name << ","<< "age: " << customer.age  << ","<< "clientid: " << customer.clientid  << ","<< "addr: " << customer.addr  << ","<< ")";return debug;
}int main(int argc,char*argv[])
{QCoreApplication app(argc, argv);Customer customer = { "Liming", 22, "677888", "北京市海淀区"};qDebug() << "Customer info" << customer;return app.exec();
}

QDebugStateSaver类可以保存QDebug的配置,并在销毁的时候自动恢复。使用它我们可以避免在操作符重载的时候破坏QDebug中的配置。

Chapter4 Qt 自定义日志类($$$)

https://blog.csdn.net/qq_45662588/article/details/116259372

Chapter5 简单易用的Qt日志模块($$$)

原文链接:https://blog.csdn.net/lm409/article/details/74908484

引言

项目中需求一日志模块,主要实现两大功能:1.自动打印信息至日志文件;2.软件意外退出时保留信息以便跟踪问题。
本文结合了 Qt 自定义日志工具 和 让程序在崩溃时体面的退出之CallStack 提供的方法,补充实现了文章中未具体给出的管理日志文件大小和数量的功能。

环境:vs2012+Qt5.2(注:Qt5.5之后引入qInfo(),影响不大)

一、日志实现方法

基本原理是使用 qInstallMessageHandler()接管qDebug(), qWarning()等调试信息,然后将信息流存储至本地日志文件,管理日志文件。
代码在原作者基础上做了部分调整:
1.更改日志存储名称格式,用QDateTime取代QDate,以避免当日记录多条日志时的覆盖问题;
2.增加日志文件个数的判断;
3.增加日志文件大小的检测;
4.屏蔽根据修改日期保存日志机制,以免在不同日期开启软件后冲掉以前的有用log,仅凭文件大小另存log文件,然后控制文件数量。

代码实现

LogHandler.cpp

#include "LogHandler.h"#include <stdio.h>
#include <stdlib.h>
#include <QDebug>
#include <QDateTime>
#include <QMutexLocker>
#include <QtGlobal>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QTimer>
#include <QTextStream>
#include <iostream>#define LOGLIMIT_NUM 5  //日志文件存档个数
#define LOGLIMIT_SIZE 500   //单个日志文件存档大小限制,单位KB
/*************************************************************************************************************                                                                                                          **                                               LogHandlerPrivate                                          **                                                                                                          ************************************************************************************************************/
struct LogHandlerPrivate {LogHandlerPrivate();~LogHandlerPrivate();// 打开日志文件 protocal.log,如果日志文件不是当天创建的,则使用创建日期把其重命名为 yyyy-MM-dd.log,并重新创建一个 protocal.logvoid openAndBackupLogFile();// 消息处理函数static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);// 如果日志所在目录不存在,则创建void makeSureLogDirectory() const;// 检测当前日志文件大小void checkLogFiles();QDir   logDir;              // 日志文件夹QTimer renameLogFileTimer;  // 重命名日志文件使用的定时器QTimer flushLogFileTimer;   // 刷新输出到日志文件的定时器QDateTime  logFileCreatedDate;  // 日志文件创建的时间static QFile *logFile;      // 日志文件static QTextStream *logOut; // 输出日志的 QTextStream,使用静态对象就是为了减少函数调用的开销static QMutex logMutex;     // 同步使用的 mutex
};// 初始化 static 变量
QMutex LogHandlerPrivate::logMutex;
QFile* LogHandlerPrivate::logFile = NULL;
QTextStream* LogHandlerPrivate::logOut = NULL;LogHandlerPrivate::LogHandlerPrivate() {logDir.setPath("Log"); // TODO: 日志文件夹的路径,为 exe 所在目录下的 log 文件夹,可从配置文件读取QString logPath = logDir.absoluteFilePath("protocal.log"); // 日志的路径// 日志文件创建的时间// QFileInfo::created(): On most Unix systems, this function returns the time of the last status change.// 所以不能运行时使用这个函数检查创建时间,因为会在运行时变化,所以在程序启动时保存下日志文件创建的时间logFileCreatedDate = QFileInfo(logPath).lastModified();//QString temp= logFileCreatedDate.toString("yyyy-MM-dd hh:mm:ss");// 打开日志文件,如果不是当天创建的,备份已有日志文件openAndBackupLogFile();// 五分钟检查一次日志文件创建时间renameLogFileTimer.setInterval(1000 * 60 * 5); // TODO: 可从配置文件读取//renameLogFileTimer.setInterval(1000*60); // 为了快速测试看到日期变化后是否新创建了对应的日志文件,所以 1 分钟检查一次renameLogFileTimer.start();QObject::connect(&renameLogFileTimer, &QTimer::timeout, [this] {QMutexLocker locker(&LogHandlerPrivate::logMutex);openAndBackupLogFile();});// 定时刷新日志输出到文件,尽快的能在日志文件里看到最新的日志flushLogFileTimer.setInterval(1000); // TODO: 可从配置文件读取flushLogFileTimer.start();QObject::connect(&flushLogFileTimer, &QTimer::timeout, [this] {// qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); // 测试不停的写入内容到日志文件QMutexLocker locker(&LogHandlerPrivate::logMutex);
//         if (NULL != logOut) {
//             logOut->flush();
//         }checkLogFiles();//每秒检查一次文件是否超过限制大小});
}LogHandlerPrivate::~LogHandlerPrivate() {if (NULL != logFile) {logFile->flush();logFile->close();delete logOut;delete logFile;// 因为他们是 static 变量logOut  = NULL;logFile = NULL;}
}// 打开日志文件 protocal.log,如果不是当天创建的,则使用创建日期把其重命名为 yyyy-MM-dd_hhmmss.log,并重新创建一个 protocal.log
void LogHandlerPrivate::openAndBackupLogFile() {// 总体逻辑:// 1. 程序启动时 logFile 为 NULL,初始化 logFile,有可能是同一天打开已经存在的 logFile,所以使用 Append 模式// 2. logFileCreatedDate is null, 说明日志文件在程序开始时不存在,所以记录下创建时间// 3. 程序运行时检查如果 logFile 的创建日期和当前日期不相等,则使用它的创建日期重命名,然后再生成一个新的 protocal.log 文件// 4. 检查日志文件超过 LOGLIMIT_NUM 个,删除最早的makeSureLogDirectory(); // 如果日志所在目录不存在,则创建QString logPath = logDir.absoluteFilePath("protocal.log"); // 日志的路径// [[1]] 程序启动时 logFile 为 NULLif (NULL == logFile) {logFile = new QFile(logPath);logOut  = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) ?  new QTextStream(logFile) : NULL;if (NULL != logOut) {logOut->setCodec("UTF-8");}// [[2]] 如果文件是第一次创建,则创建日期是无效的,把其设置为当前日期if (logFileCreatedDate.isNull()) {logFileCreatedDate = QDateTime::currentDateTime();}}// [[3]] 程序运行时如果创建日期不是当前日期,则使用创建日期重命名,并生成一个新的 protocal.log//不使用该特性,以免在不同日期开启软件后冲掉以前的有用log,仅凭文件大小另存log文件,见checkLogFiles
//     if (logFileCreatedDate.date() != QDate::currentDate()) {
//         logFile->flush();
//         logFile->close();
//         delete logOut;
//         delete logFile;
// 
//         QString newLogPath = logDir.absoluteFilePath(logFileCreatedDate.toString("yyyy-MM-dd_hhmmss.log"));;
//         QFile::copy(logPath, newLogPath); // Bug: 按理说 rename 会更合适,但是 rename 时最后一个文件总是显示不出来,需要 killall Finder 后才出现
//         QFile::remove(logPath); // 删除重新创建,改变创建时间
// 
//         logFile = new QFile(logPath);
//         logOut  = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ?  new QTextStream(logFile) : NULL;
//         logFileCreatedDate = QDateTime::currentDateTime();
// 
//         if (NULL != logOut) {
//             logOut->setCodec("UTF-8");
//         }
//  }// [[4]] 检查日志文件超过 LOGLIMIT_NUM 个,删除最早的logDir.setFilter(QDir::Files);logDir.setNameFilters(QStringList() << "*.log");//根据文件后缀过滤日志文件QFileInfoList logFiles = logDir.entryInfoList();for (int i = 0; i < logFiles.length() - LOGLIMIT_NUM; ++i)QFile::remove(logFiles[i].absoluteFilePath());//根据文件名称进一步过滤//  QMap<QDateTime, QString> fileDates;//  for (int i = 0; i < logFiles.length(); ++i)//  {//      QString name = logFiles[i].baseName();//      QDateTime fileDateTime = QDateTime::fromString(name, "yyyy-MM-dd");// //      if (fileDateTime.isValid())//          fileDates.insert(fileDateTime, logFiles[i].absoluteFilePath());//  }//  QList<QString> fileDateNames = fileDates.values();//  for (int i = 0; i < fileDateNames.length() - LOGFILESLIMIT; ++i)//      QFile::remove(fileDateNames[i]);        
}// 如果日志所在目录不存在,则创建
void LogHandlerPrivate::makeSureLogDirectory() const {if (!logDir.exists()) {logDir.mkpath("."); // 可以递归的创建文件夹}
}// 检测当前日志文件大小
void LogHandlerPrivate::checkLogFiles() {// 如果 protocal.log 文件大小超过5M,重新创建一个日志文件,原文件存档为yyyy-MM-dd_hhmmss.logif (logFile->size() > 1024*LOGLIMIT_SIZE) {logFile->flush();logFile->close();delete logOut;delete logFile;QString logPath = logDir.absoluteFilePath("protocal.log"); // 日志的路径QString newLogPath = logDir.absoluteFilePath(logFileCreatedDate.toString("yyyy-MM-dd_hhmmss.log"));;QFile::copy(logPath, newLogPath); // Bug: 按理说 rename 会更合适,但是 rename 时最后一个文件总是显示不出来,需要 killall Finder 后才出现QFile::remove(logPath); // 删除重新创建,改变创建时间logFile = new QFile(logPath);logOut  = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ?  new QTextStream(logFile) : NULL;logFileCreatedDate = QDateTime::currentDateTime();if (NULL != logOut) {logOut->setCodec("UTF-8");}}
}// 消息处理函数
void LogHandlerPrivate::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) {QMutexLocker locker(&LogHandlerPrivate::logMutex);QString level;switch (type) {case QtDebugMsg:level = "Debug";break;
//     case QtInfoMsg://This function was introduced in Qt 5.5.
//         level = "Info ";
//         break;case QtWarningMsg:level = "Warning";break;case QtCriticalMsg:level = "Error";break;case QtFatalMsg:level = "Fatal";break;default:;}// 输出到标准输出QByteArray localMsg = msg.toLocal8Bit();//std::cout << std::string(localMsg) << std::endl;if (NULL == LogHandlerPrivate::logOut) {return;}// 输出到日志文件, 格式: 时间 - [Level] (文件名:行数, 函数): 消息QString fileName = context.file;int index = fileName.lastIndexOf(QDir::separator());fileName = fileName.mid(index + 1);(*LogHandlerPrivate::logOut) << QString("%1 - [%2] (%3:%4): %5\n").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(level).arg(fileName).arg(context.line)/*.arg(context.function)*/.arg(msg);logOut->flush();//直接刷新到文件
}/*************************************************************************************************************                                                                                                          **                                               LogHandler                                                 **                                                                                                          ************************************************************************************************************/
LogHandler::LogHandler() : d(NULL) {
}LogHandler::~LogHandler() {
}void LogHandler::installMessageHandler() {QMutexLocker locker(&LogHandlerPrivate::logMutex);if (NULL == d) {d = new LogHandlerPrivate();qInstallMessageHandler(LogHandlerPrivate::messageHandler); // 给 Qt 安装自定义消息处理函数}
}void LogHandler::release() {QMutexLocker locker(&LogHandlerPrivate::logMutex);qInstallMessageHandler(0);delete d;d = NULL;
}

二、崩溃处理

让程序在崩溃时体面的退出之总结博主在系列文章中做了详尽的说明。
我的应用目的是在程序崩溃时能体面退出,然后记录基本的CallStack信息到日志文件,所以只用到了前面两部分内容。在上文的基础上,用qCritical()或其他方法输出Crash信息和CallStack信息即可。

代码实现

LogHandler.cpp

//程式异常捕获
LONG ApplicationCrashHandler(EXCEPTION_POINTERS *pException){/****保存数据代码****/// 创建Dump文件目录QDir   DumpDir; DumpDir.setPath("Log");LPCWSTR DumpPath = (const wchar_t*) DumpDir.absoluteFilePath("ProtocolTester.dmp").utf16();// Dump文件的路径CreateDumpFile(DumpPath, pException);  // 确保有足够的栈空间
#ifdef _M_IX86  if (pException->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)  {  static char TempStack[1024 * 128];  __asm mov eax,offset TempStack[1024 * 128];  __asm mov esp,eax;  }  
#endif    CrashInfo crashinfo = GetCrashInfo(pException->ExceptionRecord);  // 输出Crash信息qCritical() << "ErrorCode: " << crashinfo.ErrorCode << endl;  qCritical() << "Address: " << crashinfo.Address << endl;  qCritical() << "Flags: " << crashinfo.Flags << endl;  vector<CallStackInfo> arrCallStackInfo = GetCallStack(pException->ContextRecord);  // 输出CallStackqCritical() << "CallStack: " << endl;  for (vector<CallStackInfo>::iterator i = arrCallStackInfo.begin(); i != arrCallStackInfo.end(); ++i)  {  CallStackInfo callstackinfo = (*i);  qCritical() << callstackinfo.MethodName << "() : [" << callstackinfo.ModuleName << "] (File: " << callstackinfo.FileName << " @Line " << callstackinfo.LineNumber << ")" << endl;  }//这里弹出一个错误对话框并退出程序EXCEPTION_RECORD* record = pException->ExceptionRecord;QString errCode(QString::number(record->ExceptionCode,16)),errAdr(QString::number((uint)record->ExceptionAddress,16)),errMod;QMessageBox::critical(NULL,QStringLiteral("Error"),QStringLiteral("<FONT size=4><div><b>很抱歉,程序出错了。</b><br/></div>")+QStringLiteral("<div>错误代码:%1</div><div>错误地址:%2</div></FONT>").arg(errCode).arg(errAdr),QMessageBox::Ok);return EXCEPTION_EXECUTE_HANDLER;
}

小结

本文实现了一个轻量的Qt日志模块,功能肯定是没有log4qt或log4cxx等强大,但也基本满足了项目应用需求,想了解log4qt也可以查看DevBean豆子大神的github

让程序在崩溃时体面的退出之CallStack

Chapter6 Qt/C++开源作品39-日志输出增强版V2022($$$)

原文链接:https://blog.csdn.net/feiyangqingyun/article/details/121314920

一、前言

之前已经开源过基础版本,近期根据客户需求和自己的项目需求,提炼出通用需求部分,对整个日志重定向输出类重新规划和重写代码。

用Qt这个一站式超大型GUI超市做开发已经十二年了,陆陆续续开发过至少几十个程序,除了一些算不算项目的小工具外,大部分的程序都需要有个日志的输出功能,希望可以将程序的运行状态存储到文本文件或者数据库或者做其他处理等,Qt对这个日志输出也做了很好的封装,在Qt4是qInstallMsgHandler,Qt5及Qt6里边是qInstallMessageHandler,有了这个神器,只要在你的项目中所有qDebug qInfo等输出的日志信息,都会重定向接收到。

网上大部分人写的demo都是接收到输出打印日志存储到文本文件,其实这就带给很多人误解,容易产生以为日志只能输出到文本文件,其实安装了日志钩子以后,拿到了所有调试打印信息,你完全可以用来存储到数据库及输出html有颜色区分格式的文件,或者网络转发输出(尤其适用于嵌入式linux无界面程序,现场不方便外接调试打印的设备)。

做过的这么多项目中,Qt4、Qt5、Qt6的都有,我一般保留四个版本,4.8.7,为了兼容Qt4, 5.7.0,最后的支持XP的版本, 最新的长期支持版本5.15.2 最高的新版本6.2.1。毫无疑问,我要封装的这个日志类,也要同时支持Qt4、Qt5、Qt6的,而且提供友好的接口。

二、主要功能

支持动态启动和停止。
支持日志存储的目录。
支持网络发出打印日志。
支持输出日志上下文信息比如所在代码文件、行号、函数名等。
支持设置日志文件大小限制,超过则自动分文件,默认128kb。
支持按照日志行数自动分文件,和日志大小条件互斥。
可选按照日期时间区分文件名存储日志。
日志文件命名规则优先级:行数》大小》日期。
自动加锁支持多线程。
可以分别控制哪些类型的日志需要重定向输出。
支持Qt4+Qt5+Qt6,开箱即用。
使用方式最简单,调用函数start()启动服务,stop()停止服务。

三、效果图

在这里插入图片描述

四、开源主页

以上作品完整源码下载都在开源主页,会持续不断更新作品数量和质量,欢迎各位关注。
本开源项目已经成功升级到V2.0版本,分门别类,图文并茂,保你爽到爆。
Qt开源武林秘籍开发经验,看完学完,20K起薪,没有找我!
国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
国际站点:https://github.com/feiyangqingyun/QWidgetDemo
开源秘籍:https://gitee.com/feiyangqingyun/qtkaifajingyan
个人主页:https://qtchina.blog.csdn.net/
视频主页:https://space.bilibili.com/687803542

五、核心代码

#pragma execution_character_set("utf-8")#include "savelog.h"
#include "qmutex.h"
#include "qdir.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"))
#define QDATETIMS qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))//日志重定向
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
void Log(QtMsgType type, const QMessageLogContext &context, const QString &msg)
#else
void Log(QtMsgType type, const char *msg)
#endif
{//加锁,防止多线程中qdebug太频繁导致崩溃static QMutex mutex;QMutexLocker locker(&mutex);QString content;//这里可以根据不同的类型加上不同的头部用于区分int msgType = SaveLog::Instance()->getMsgType();switch (type) {case QtDebugMsg:if ((msgType & MsgType_Debug) == MsgType_Debug) {content = QString("Debug %1").arg(msg);}break;
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))case QtInfoMsg:if ((msgType & MsgType_Info) == MsgType_Info) {content = QString("Infox %1").arg(msg);}break;
#endifcase QtWarningMsg:if ((msgType & MsgType_Warning) == MsgType_Warning) {content = QString("Warnx %1").arg(msg);}break;case QtCriticalMsg:if ((msgType & MsgType_Critical) == MsgType_Critical) {content = QString("Error %1").arg(msg);}break;case QtFatalMsg:if ((msgType & MsgType_Fatal) == MsgType_Fatal) {content = QString("Fatal %1").arg(msg);}break;}//没有内容则返回if (content.isEmpty()) {return;}//加上打印代码所在代码文件、行号、函数名
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))if (SaveLog::Instance()->getUseContext()) {int line = context.line;QString file = context.file;QString function = context.function;if (line > 0) {content = QString("行号: %1  文件: %2  函数: %3\n%4").arg(line).arg(file).arg(function).arg(content);}}
#endif//还可以将数据转成html内容分颜色区分//将内容传给函数进行处理SaveLog::Instance()->save(content);
}QScopedPointer<SaveLog> SaveLog::self;
SaveLog *SaveLog::Instance()
{if (self.isNull()) {static 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)));isRun = false;maxRow = currentRow = 0;maxSize = 128;toNet = false;useContext = true;//全局的文件对象,在需要的时候打开而不是每次添加日志都打开file = new QFile(this);//默认取应用程序根目录path = qApp->applicationDirPath();//默认取应用程序可执行文件名称QString str = qApp->applicationFilePath();QStringList list = str.split("/");name = list.at(list.count() - 1).split(".").at(0);fileName = "";//默认所有类型都输出msgType = MsgType(MsgType_Debug | MsgType_Info | MsgType_Warning | MsgType_Critical | MsgType_Fatal);
}SaveLog::~SaveLog()
{file->close();
}void SaveLog::openFile(const QString &fileName)
{//当文件名改变时才新建和打开文件而不是每次都打开文件(效率极低)或者一开始打开文件if (this->fileName != fileName) {this->fileName = fileName;//先关闭之前的if (file->isOpen()) {file->close();}//重新设置新的日志文件file->setFileName(fileName);//以 Append 追加的形式打开file->open(QIODevice::WriteOnly | QIODevice::Append | QFile::Text);}
}bool SaveLog::getUseContext()
{return this->useContext;
}MsgType SaveLog::getMsgType()
{return this->msgType;
}//安装日志钩子,输出调试信息到文件,便于调试
void SaveLog::start()
{if (isRun) {return;}isRun = true;
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))qInstallMessageHandler(Log);
#elseqInstallMsgHandler(Log);
#endif
}//卸载日志钩子
void SaveLog::stop()
{if (!isRun) {return;}isRun = false;this->clear();
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))qInstallMessageHandler(0);
#elseqInstallMsgHandler(0);
#endif
}void SaveLog::clear()
{currentRow = 0;fileName.clear();if (file->isOpen()) {file->close();}
}void SaveLog::save(const QString &content)
{//如果重定向输出到网络则通过网络发出去,否则输出到日志文件if (toNet) {emit send(content);} else {//目录不存在则先新建目录QDir dir(path);if (!dir.exists()) {dir.mkdir(path);}//日志存储规则有多种策略 优先级 行数>大小>日期//1: 设置了最大行数限制则按照行数限制来//2: 设置了大小则按照大小来控制日志文件//3: 都没有设置都存储到日期命名的文件,只有当日期变化了才会切换到新的日志文件bool needOpen = false;if (maxRow > 0) {currentRow++;if (fileName.isEmpty()) {needOpen = true;} else if (currentRow >= maxRow) {needOpen = true;}} else if (maxSize > 0) {//1MB=1024*1024 经过大量测试 QFile().size() 方法速度非常快//首次需要重新打开文件以及超过大小需要重新打开文件if (fileName.isEmpty()) {needOpen = true;} else if (file->size() > (maxSize * 1024)) {needOpen = true;}} else {//日期改变了才会触发QString fileName = QString("%1/%2_log_%3.txt").arg(path).arg(name).arg(QDATE);openFile(fileName);}if ((maxRow > 0 || maxSize > 0) && needOpen) {currentRow = 0;QString fileName = QString("%1/%2_log_%3.txt").arg(path).arg(name).arg(QDATETIMS);openFile(fileName);}//用文本流的输出速度更快QTextStream stream(file);stream << content << "\n";}
}void SaveLog::setMaxRow(int maxRow)
{//这里可以限定最大最小值if (maxRow >= 0) {this->maxRow = maxRow;this->clear();}
}void SaveLog::setMaxSize(int maxSize)
{//这里可以限定最大最小值if (maxSize >= 0) {this->maxSize = maxSize;this->clear();}
}void SaveLog::setListenPort(int listenPort)
{SendLog::Instance()->setListenPort(listenPort);
}void SaveLog::setToNet(bool toNet)
{this->toNet = toNet;if (toNet) {SendLog::Instance()->start();} else {SendLog::Instance()->stop();}
}void SaveLog::setUseContext(bool useContext)
{this->useContext = useContext;
}void SaveLog::setPath(const QString &path)
{this->path = path;
}void SaveLog::setName(const QString &name)
{this->name = name;
}void SaveLog::setMsgType(const MsgType &msgType)
{this->msgType = msgType;
}//网络发送日志数据类
QScopedPointer<SendLog> SendLog::self;
SendLog *SendLog::Instance()
{if (self.isNull()) {static QMutex mutex;QMutexLocker locker(&mutex);if (self.isNull()) {self.reset(new SendLog);}}return self.data();
}SendLog::SendLog(QObject *parent) : QObject(parent)
{listenPort = 6000;socket = NULL;//实例化网络通信服务器对象server = new QTcpServer(this);connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
}SendLog::~SendLog()
{if (socket != NULL) {socket->disconnectFromHost();}server->close();
}void SendLog::newConnection()
{//限定就一个连接while (server->hasPendingConnections()) {socket = server->nextPendingConnection();}
}void SendLog::setListenPort(int listenPort)
{this->listenPort = listenPort;
}void SendLog::start()
{//启动端口监听
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))server->listen(QHostAddress::AnyIPv4, listenPort);
#elseserver->listen(QHostAddress::Any, listenPort);
#endif
}void SendLog::stop()
{if (socket != NULL) {socket->disconnectFromHost();socket = NULL;}server->close();
}void SendLog::send(const QString &content)
{if (socket != NULL && socket->isOpen()) {socket->write(content.toUtf8());//socket->flush();}
}

相关文章:

Qt重定向QDebug,Qt/C++开源作品39-日志输出增强版V2022

Qt重定向QDebug&#xff0c;自定义一个简易的日志管理类 Chapter1 Qt重定向QDebug&#xff0c;自定义一个简易的日志管理类0.前言1.最简单的操作运行结果2.实现一个简易的日志管理类 Chapter2 Qt::Qt Log日志模块Qt Log日志模块官方解释官方Demo思路 Chapter3 QT日志模块的个性…...

linux入门---多线程的控制

目录标题 线程库pthread_create如何一次性创建多个线程线程的终止线程的等待线程取消分离线程如何看待其他语言支持的多线程线程id的本质线程的局部存储线程的封装 线程库 要想控制线程就得使用原生线程库也可以将其称为pthread库&#xff0c;这个库是遵守posix标准的&#xf…...

基于android的 rk3399 同时支持多个USB摄像头

基于android的 rk3399 同时支持多个USB摄像头 一、前文二、CameraHal_Module.h三、CameraHal_Module.cpp四、编译&烧录Image五、App验证 一、前文 Android系统默认支持2个摄像头&#xff0c;一个前置摄像头&#xff0c;一个后置摄像头 需要支持数量更多的摄像头&#xff0…...

【Qt之控件QTreeView】设置单元格高度、设置图标尺寸

设置列宽 设置高度 自定义代理 继承QItemDelegate&#xff0c;实现sizeHint ()方法&#xff0c;设置自定义委托。 class itemDelegate : public QItemDelegate {Q_OBJECTpublic:explicit itemDelegate(QObject *parent 0) : QItemDelegate(parent){}~itemDelegate(){}virtua…...

力扣42.接雨水(java,暴力法、前缀和解法)

Problem: 42. 接雨水 文章目录 思路解题方法复杂度Code 思路 要能接住雨水&#xff0c;感性的认知就是要形成一个“下凹区域”&#xff0c;则此时我们就要比较当前柱子和其左右柱子高度的关系&#xff0c;易得一个关键的式子&#xff1a;当前小区域的积水 min&#xff08;当前…...

hdlbits系列verilog解答(移位寄存器)-23

文章目录 一、问题描述二、verilog源码三、仿真结果 一、问题描述 您将获得一个具有两个输入和一个输出的模块 my_dff &#xff08;实现 D 触发器&#xff09;。实例化其中的三个&#xff0c;然后将它们链接在一起以形成长度为 3 的移位寄存器。端口 clk 需要连接到所有实例。…...

Linux命令记载

服务器基本操作 SSH登录服务器 ssh -p 端口号 用户名服务器IP 输入密码SFTP上传文件 #输入密码 #使用get命令下载远程服务器的文件&#xff0c;比如/usr/test.txt sftp>get /usr/test.txt#使用put命令上传本地文件到服务器&#xff0c;比如/usr/test1.txt sftp> put /…...

Flume 快速入门【概述、安装、拦截器】

文章目录 什么是 Flume&#xff1f;Flume 组成Flume 安装Flume 配置任务文件应用示例启动 Flume 采集任务 Flume 拦截器编写 Flume 拦截器拦截器应用 什么是 Flume&#xff1f; Flume 是一个开源的数据采集工具&#xff0c;最初由 Apache 软件基金会开发和维护。它的主要目的是…...

【pandas技巧】group by+agg+transform函数

目录 1. group by单个字段单个聚合 2. group by单个字段多个聚合 3. group by多个字段单个聚合 4. group by多个字段多个聚合 5. transform函数 studentsgradesexscoremoney0小狗小学部female958441小猫小学部male938362小鸭初中部male838543小兔小学部female909314小花小…...

一文解读WordPress网站的各类缓存-老白博客

缓存是一种重要的WordPress优化手段&#xff0c;用于提高网站的性能和加载速度。减少计算量&#xff0c;有效提升响应速度&#xff0c;让有限的资源服务更多的用户。本文老白博客便从自己的使用简单给大家介绍下WordPress的缓存&#xff0c;包括 站点缓存&#xff08;Page Cach…...

从零开始:开发直播商城APP的技术指南

时下&#xff0c;直播商城APP已经成了线上购物、电子商务的核心组成&#xff0c;本文将为您提供一个全面的技术指南&#xff0c;帮助您从零开始开发一个直播商城APP。我们将涵盖所有关键方面&#xff0c;包括技术堆栈、功能模块、用户体验和安全性。 第一部分&#xff1a;技术…...

GZ035 5G组网与运维赛题第6套

2023年全国职业院校技能大赛 GZ035 5G组网与运维赛项&#xff08;高职组&#xff09; 赛题第6套 一、竞赛须知 1.竞赛内容分布 竞赛模块1--5G公共网络规划部署与开通&#xff08;35分&#xff09; 子任务1&#xff1a;5G公共网络部署与调试&#xff08;15分&#xff09; …...

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测(自注意力机制)

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09; 目录 分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matla…...

【Qt】QString怎么转成int

2023年10月29日&#xff0c;周日晚上 第一种方法 这种方法会尝试将 QString 对象转换为 int 类型。如果转换成功&#xff0c;将返回转换后的 int 值&#xff1b;如果转换失败&#xff08;例如&#xff0c;字符串中包含非数字字符&#xff09;&#xff0c;则返回 0。 QString…...

ubuntu 22.04 安装python-pcl

ubuntu 22.04 安装python-pcl 安装python-pcl修复bug 由于python-pcl库基本已经停止维护&#xff0c;所以Ubuntu22.04 在使用pip install python-pcl安装的时候会出现版本不适配的原因 安装python-pcl 使用Ubuntu22系统自带python3安装python-pcl&#xff0c;随后将下载的包拷…...

【题解】[GenshinOI Round 3 ]P9817 lmxcslD

题目传送门 分析 看到这道题我一开始是有点懵的&#xff0c;但是看了看数据范围&#xff0c;发现有几个点有 n 为质数 的特殊性质&#xff0c;结论先行&#xff0c;大胆猜测是不是可以贪心&#xff0c;所以先打了一个最傻的代码上去试试. void solve(){cin >> n >&…...

在pycharm中,远程操作服务器上的jupyter notebook

一、使用场景 现在我们有两台电脑&#xff0c;一台是拥有高算力的服务器&#xff0c;另一台是普通的轻薄笔记本电脑。如何在服务器上运行jupyter notebook&#xff0c;同时映射到笔记本电脑上的pycharm客户端中进行操作呢&#xff1f; 二、软件 pycharm专业版&#xff0c;jupy…...

SQL 运算符

SQL 运算符 运算符是保留字或主要用于 SQL 语句的 WHERE 子句中的字符&#xff0c;用于执行操作&#xff0c;例如&#xff1a;比较和算术运算。 这些运算符用于指定 SQL 语句中的条件&#xff0c;并用作语句中多个条件的连词。 常见运算符有以下几种&#xff1a; 算术运算符比…...

中间件安全-CVE 复现K8sDockerJettyWebsphere漏洞复现

目录 服务攻防-中间件安全&CVE 复现&K8s&Docker&Jetty&Websphere中间件-K8s中间件-Jetty漏洞复现CVE-2021-28164-路径信息泄露漏洞CVE-2021-28169双重解码信息泄露漏洞CVE-2021-34429路径信息泄露漏洞 中间件-Docker漏洞复现守护程序 API 未经授权访问漏洞…...

系列九、什么是Spring bean

一、什么是Spring bean 一句话&#xff0c;被Spring容器管理的bean就是Spring bean。...

轻量封装WebGPU渲染系统示例<4>-CubeMap/天空盒(源码)

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/version-1.01/src/voxgpu/sample/ImgCubeMap.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 2. 高频调用与低频调用隔离。 3. 面向用户的易用性封装。 4. 渲染数据和渲染机制分离。 5. 用户…...

Linux 环境变量 二

目录 获取环境变量的后两种方法 环境变量具有全局属性 内建命令 和环境变量相关的命令 c语言访问地址 重新理解地址 地址空间 获取环境变量的后两种方法 main函数的第三个参数 &#xff1a;char* env[ ] 也是一个指针数组&#xff0c;我们可以把它的内容打印出来看看。 …...

Beyond Compare4 30天试用到期的解决办法

相信很多小伙伴都有在使用Beyond Compare 4软件&#xff0c;如果我们没有激活该软件&#xff0c;就只有30天的评估使用期&#xff0c;那么过了这30天后我们怎么继续使用呢&#xff1f;下面小编就来为大家介绍方法。 打开Beyond Compare4&#xff0c;提示已经超出30天试用期限制…...

sentinel规则持久化-规则同步nacos-最标准配置

官方参考文档&#xff1a; 动态规则扩展 alibaba/Sentinel Wiki GitHub 需要修改的代码如下&#xff1a; 为了便于后续版本集成nacos&#xff0c;简单讲一下集成思路 1.更改pom 修改sentinel-datasource-nacos的范围 将 <dependency><groupId>com.alibaba.c…...

【Linux】tail命令使用

tail 命令可用于查看文件的内容&#xff0c;有一个常用的参数 -f 常用于查阅正在改变的日志文件。 语法 tail [参数] [文件] tail命令 -Linux手册页 著者 由保罗鲁宾、大卫麦肯齐、伊恩兰斯泰勒和吉姆梅耶林撰写。 命令选项及作用 执行令 tail --help 执行命令结果 参…...

【数据结构】面试OJ题——时间复杂度2

目录 一&#xff1a;移除元素 思路&#xff1a; 二&#xff1a;删除有序数组中的重复项 思路&#xff1a; 三&#xff1a;合并两个有序数组 思路1&#xff1a; 什么&#xff1f;你不知道qsort&#xff08;&#xff09; 思路2&#xff1a; 一&#xff1a;移除元素 27. 移…...

LibreOffice编辑excel文档如何在单元格中输入手动换行符

用WPS编辑excel文档的时候&#xff0c;要在单元格中输入手动换行符&#xff0c;可以先按住Alt键&#xff0c;然后回车。 而用LibreOffice编辑excel文档&#xff0c;要在单元格中输入手动换行符&#xff0c;可以先按住Ctrl键&#xff0c;然后回车。例如&#xff1a;...

ideaSSM在线商务管理系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 SSM 在线商务管理系统是一套完善的信息管理系统&#xff0c;结合SSM框架和bootstrap完成本系统&#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发&#xff09;&#xff0c;系统具有完整的源代码 和数据库&#xff0c;系统主…...

数据结构 | 顺序表专题

数据结构 | 顺序表专题 文章目录 数据结构 | 顺序表专题课前准备1. 目标2. 需要的储备知识3. 数据结构相关概念 开始顺序表1、顺序表的概念及结构2、顺序表分类3、动态顺序表的实现初始化顺序表打印顺序表内存容量的检查顺序表的尾插顺序表的尾删顺序表的头插顺序表的头删在顺序…...

C++可视化 有穷自动机NFA 有穷自动机DFA

一、项目介绍 根据正则表达式,可视化显示NFA&#xff0c;DFA&#xff1b;词法分析程序 二、项目展示...