2021-10-30 11:20:00 +00:00
|
|
|
|
#pragma execution_character_set("utf-8")
|
|
|
|
|
|
|
|
|
|
#include "savelog.h"
|
2019-10-14 02:24:26 +00:00
|
|
|
|
#include "qmutex.h"
|
2021-10-08 02:51:37 +00:00
|
|
|
|
#include "qdir.h"
|
2019-10-14 02:24:26 +00:00
|
|
|
|
#include "qfile.h"
|
|
|
|
|
#include "qtcpsocket.h"
|
|
|
|
|
#include "qtcpserver.h"
|
|
|
|
|
#include "qdatetime.h"
|
|
|
|
|
#include "qapplication.h"
|
|
|
|
|
#include "qtimer.h"
|
2022-01-13 03:27:55 +00:00
|
|
|
|
#include "qtextstream.h"
|
2019-10-14 02:24:26 +00:00
|
|
|
|
#include "qstringlist.h"
|
|
|
|
|
|
|
|
|
|
#define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd"))
|
2021-10-30 11:20:00 +00:00
|
|
|
|
#define QDATETIMS qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))
|
2019-10-14 02:24:26 +00:00
|
|
|
|
|
|
|
|
|
//日志重定向
|
2021-10-13 06:15:18 +00:00
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
|
|
|
|
|
void Log(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
2019-10-14 02:24:26 +00:00
|
|
|
|
#else
|
2021-10-13 06:15:18 +00:00
|
|
|
|
void Log(QtMsgType type, const char *msg)
|
2019-10-14 02:24:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
//加锁,防止多线程中qdebug太频繁导致崩溃
|
2020-05-28 01:54:00 +00:00
|
|
|
|
static QMutex mutex;
|
2019-10-14 02:24:26 +00:00
|
|
|
|
QMutexLocker locker(&mutex);
|
|
|
|
|
QString content;
|
|
|
|
|
|
|
|
|
|
//这里可以根据不同的类型加上不同的头部用于区分
|
2021-10-30 11:20:00 +00:00
|
|
|
|
int msgType = SaveLog::Instance()->getMsgType();
|
2019-10-14 02:24:26 +00:00
|
|
|
|
switch (type) {
|
2021-05-30 07:59:42 +00:00
|
|
|
|
case QtDebugMsg:
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if ((msgType & MsgType_Debug) == MsgType_Debug) {
|
|
|
|
|
content = QString("Debug %1").arg(msg);
|
|
|
|
|
}
|
2021-05-30 07:59:42 +00:00
|
|
|
|
break;
|
2021-11-14 05:06:47 +00:00
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
|
2021-10-30 11:20:00 +00:00
|
|
|
|
case QtInfoMsg:
|
|
|
|
|
if ((msgType & MsgType_Info) == MsgType_Info) {
|
|
|
|
|
content = QString("Infox %1").arg(msg);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
2021-05-30 07:59:42 +00:00
|
|
|
|
case QtWarningMsg:
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if ((msgType & MsgType_Warning) == MsgType_Warning) {
|
|
|
|
|
content = QString("Warnx %1").arg(msg);
|
|
|
|
|
}
|
2021-05-30 07:59:42 +00:00
|
|
|
|
break;
|
|
|
|
|
case QtCriticalMsg:
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if ((msgType & MsgType_Critical) == MsgType_Critical) {
|
|
|
|
|
content = QString("Error %1").arg(msg);
|
|
|
|
|
}
|
2021-05-30 07:59:42 +00:00
|
|
|
|
break;
|
|
|
|
|
case QtFatalMsg:
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if ((msgType & MsgType_Fatal) == MsgType_Fatal) {
|
|
|
|
|
content = QString("Fatal %1").arg(msg);
|
|
|
|
|
}
|
2021-05-30 07:59:42 +00:00
|
|
|
|
break;
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//没有内容则返回
|
|
|
|
|
if (content.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-13 06:15:18 +00:00
|
|
|
|
//加上打印代码所在代码文件、行号、函数名
|
|
|
|
|
#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
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//还可以将数据转成html内容分颜色区分
|
2021-10-13 06:15:18 +00:00
|
|
|
|
//将内容传给函数进行处理
|
2019-10-14 02:24:26 +00:00
|
|
|
|
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)));
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
isRun = false;
|
|
|
|
|
maxRow = currentRow = 0;
|
|
|
|
|
maxSize = 128;
|
2019-10-14 02:24:26 +00:00
|
|
|
|
toNet = false;
|
2021-10-13 06:15:18 +00:00
|
|
|
|
useContext = true;
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//全局的文件对象,在需要的时候打开而不是每次添加日志都打开
|
|
|
|
|
file = new QFile(this);
|
2019-10-14 02:24:26 +00:00
|
|
|
|
//默认取应用程序根目录
|
|
|
|
|
path = qApp->applicationDirPath();
|
|
|
|
|
//默认取应用程序可执行文件名称
|
|
|
|
|
QString str = qApp->applicationFilePath();
|
|
|
|
|
QStringList list = str.split("/");
|
2023-03-20 07:04:23 +00:00
|
|
|
|
name = list.at(list.count() - 1).split(".").at(0);
|
2019-10-14 02:24:26 +00:00
|
|
|
|
fileName = "";
|
2021-10-30 11:20:00 +00:00
|
|
|
|
|
|
|
|
|
//默认所有类型都输出
|
|
|
|
|
msgType = MsgType(MsgType_Debug | MsgType_Info | MsgType_Warning | MsgType_Critical | MsgType_Fatal);
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SaveLog::~SaveLog()
|
|
|
|
|
{
|
2023-09-08 05:51:39 +00:00
|
|
|
|
this->stop();
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-13 06:15:18 +00:00
|
|
|
|
bool SaveLog::getUseContext()
|
|
|
|
|
{
|
|
|
|
|
return this->useContext;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
MsgType SaveLog::getMsgType()
|
|
|
|
|
{
|
|
|
|
|
return this->msgType;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 02:24:26 +00:00
|
|
|
|
//安装日志钩子,输出调试信息到文件,便于调试
|
|
|
|
|
void SaveLog::start()
|
|
|
|
|
{
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if (isRun) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isRun = true;
|
2021-10-13 06:15:18 +00:00
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
|
2019-10-14 02:24:26 +00:00
|
|
|
|
qInstallMessageHandler(Log);
|
2021-10-13 06:15:18 +00:00
|
|
|
|
#else
|
|
|
|
|
qInstallMsgHandler(Log);
|
2019-10-14 02:24:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//卸载日志钩子
|
|
|
|
|
void SaveLog::stop()
|
|
|
|
|
{
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if (!isRun) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isRun = false;
|
|
|
|
|
this->clear();
|
2021-10-13 06:15:18 +00:00
|
|
|
|
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
|
2019-10-14 02:24:26 +00:00
|
|
|
|
qInstallMessageHandler(0);
|
2021-10-13 06:15:18 +00:00
|
|
|
|
#else
|
|
|
|
|
qInstallMsgHandler(0);
|
2019-10-14 02:24:26 +00:00
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
void SaveLog::clear()
|
|
|
|
|
{
|
|
|
|
|
currentRow = 0;
|
|
|
|
|
fileName.clear();
|
|
|
|
|
if (file->isOpen()) {
|
|
|
|
|
file->close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 02:24:26 +00:00
|
|
|
|
void SaveLog::save(const QString &content)
|
|
|
|
|
{
|
|
|
|
|
//如果重定向输出到网络则通过网络发出去,否则输出到日志文件
|
|
|
|
|
if (toNet) {
|
2023-12-06 11:15:52 +00:00
|
|
|
|
Q_EMIT send(content);
|
2019-10-14 02:24:26 +00:00
|
|
|
|
} else {
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//目录不存在则先新建目录
|
2021-10-07 02:57:25 +00:00
|
|
|
|
QDir dir(path);
|
|
|
|
|
if (!dir.exists()) {
|
|
|
|
|
dir.mkdir(path);
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//日志存储规则有多种策略 优先级 行数>大小>日期
|
|
|
|
|
//1: 设置了最大行数限制则按照行数限制来
|
|
|
|
|
//2: 设置了大小则按照大小来控制日志文件
|
|
|
|
|
//3: 都没有设置都存储到日期命名的文件,只有当日期变化了才会切换到新的日志文件
|
|
|
|
|
bool needOpen = false;
|
|
|
|
|
if (maxRow > 0) {
|
|
|
|
|
currentRow++;
|
|
|
|
|
if (fileName.isEmpty()) {
|
|
|
|
|
needOpen = true;
|
|
|
|
|
} else if (currentRow >= maxRow) {
|
|
|
|
|
needOpen = true;
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
2021-10-30 11:20:00 +00:00
|
|
|
|
} 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);
|
|
|
|
|
}
|
2019-10-14 02:24:26 +00:00
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if ((maxRow > 0 || maxSize > 0) && needOpen) {
|
|
|
|
|
currentRow = 0;
|
|
|
|
|
QString fileName = QString("%1/%2_log_%3.txt").arg(path).arg(name).arg(QDATETIMS);
|
|
|
|
|
openFile(fileName);
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//用文本流的输出速度更快
|
|
|
|
|
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();
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
void SaveLog::setListenPort(int listenPort)
|
|
|
|
|
{
|
|
|
|
|
SendLog::Instance()->setListenPort(listenPort);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 02:24:26 +00:00
|
|
|
|
void SaveLog::setToNet(bool toNet)
|
|
|
|
|
{
|
|
|
|
|
this->toNet = toNet;
|
2021-10-30 11:20:00 +00:00
|
|
|
|
if (toNet) {
|
|
|
|
|
SendLog::Instance()->start();
|
|
|
|
|
} else {
|
|
|
|
|
SendLog::Instance()->stop();
|
|
|
|
|
}
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-13 06:15:18 +00:00
|
|
|
|
void SaveLog::setUseContext(bool useContext)
|
|
|
|
|
{
|
|
|
|
|
this->useContext = useContext;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 02:24:26 +00:00
|
|
|
|
void SaveLog::setPath(const QString &path)
|
|
|
|
|
{
|
|
|
|
|
this->path = path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SaveLog::setName(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
this->name = name;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
void SaveLog::setMsgType(const MsgType &msgType)
|
|
|
|
|
{
|
|
|
|
|
this->msgType = msgType;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 02:24:26 +00:00
|
|
|
|
|
|
|
|
|
//网络发送日志数据类
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
SendLog::SendLog(QObject *parent) : QObject(parent)
|
2019-10-14 02:24:26 +00:00
|
|
|
|
{
|
2021-10-30 11:20:00 +00:00
|
|
|
|
listenPort = 6000;
|
2019-10-14 02:24:26 +00:00
|
|
|
|
socket = NULL;
|
2021-10-30 11:20:00 +00:00
|
|
|
|
|
|
|
|
|
//实例化网络通信服务器对象
|
2019-10-14 02:24:26 +00:00
|
|
|
|
server = new QTcpServer(this);
|
|
|
|
|
connect(server, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SendLog::~SendLog()
|
|
|
|
|
{
|
|
|
|
|
if (socket != NULL) {
|
|
|
|
|
socket->disconnectFromHost();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server->close();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SendLog::newConnection()
|
|
|
|
|
{
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//限定就一个连接
|
2019-10-14 02:24:26 +00:00
|
|
|
|
while (server->hasPendingConnections()) {
|
|
|
|
|
socket = server->nextPendingConnection();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-30 11:20:00 +00:00
|
|
|
|
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);
|
|
|
|
|
#else
|
|
|
|
|
server->listen(QHostAddress::Any, listenPort);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SendLog::stop()
|
|
|
|
|
{
|
|
|
|
|
if (socket != NULL) {
|
|
|
|
|
socket->disconnectFromHost();
|
|
|
|
|
socket = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server->close();
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-14 02:24:26 +00:00
|
|
|
|
void SendLog::send(const QString &content)
|
|
|
|
|
{
|
|
|
|
|
if (socket != NULL && socket->isOpen()) {
|
|
|
|
|
socket->write(content.toUtf8());
|
2021-10-30 11:20:00 +00:00
|
|
|
|
//socket->flush();
|
2019-10-14 02:24:26 +00:00
|
|
|
|
}
|
|
|
|
|
}
|