MODIFY:精简日志
This commit is contained in:
+30
-185
@@ -2,172 +2,14 @@
|
|||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
|
|
||||||
// 初始化 static 变量
|
QMutex g_mutex;
|
||||||
QMutex LogHandlerPrivate::logMutex;
|
//QTextStream *g_in = nullptr;
|
||||||
QFile* LogHandlerPrivate::logFile = nullptr;
|
QFile g_file;
|
||||||
QTextStream* LogHandlerPrivate::logOut = nullptr;
|
|
||||||
|
|
||||||
LogHandlerPrivate::LogHandlerPrivate()
|
|
||||||
{
|
|
||||||
auto localApplicationDirPath = QCoreApplication::applicationDirPath();
|
|
||||||
logDir.setPath(localApplicationDirPath + "/log"); // TODO: 日志文件夹的路径,为 exe 所在目录下的 log 文件夹,可从配置文件读取
|
|
||||||
QString logPath = logDir.absoluteFilePath("today.log"); // 获取日志的路径
|
|
||||||
|
|
||||||
// ========获取日志文件创建的时间========
|
|
||||||
// QFileInfo::created(): On most Unix systems, this function returns the time of the last status change.
|
|
||||||
// 所以不能运行时使用这个函数检查创建时间,因为会在运行时变化,于是在程序启动时保存下日志文件的最后修改时间,
|
|
||||||
logFileCreatedDate = QFileInfo(logPath).lastModified().date(); // 若日志文件不存在,返回nullptr
|
|
||||||
|
|
||||||
// 打开日志文件,如果不是当天创建的,备份已有日志文件
|
|
||||||
openAndBackupLogFile();
|
|
||||||
|
|
||||||
// 十分钟检查一次日志文件创建时间
|
|
||||||
renameLogFileTimer.setInterval(1000 * 10); // TODO: 可从配置文件读取
|
|
||||||
renameLogFileTimer.start();
|
|
||||||
QObject::connect(&renameLogFileTimer, &QTimer::timeout, [this]
|
|
||||||
{
|
|
||||||
QMutexLocker locker(&LogHandlerPrivate::logMutex);
|
|
||||||
openAndBackupLogFile(); // 打开日志文件
|
|
||||||
checkLogFiles(); // 检测当前日志文件大小
|
|
||||||
autoDeleteLog(); // 自动删除30天前的日志
|
|
||||||
});
|
|
||||||
|
|
||||||
// 定时刷新日志输出到文件,尽快的能在日志文件里看到最新的日志
|
|
||||||
flushLogFileTimer.setInterval(1000); // TODO: 可从配置文件读取
|
|
||||||
flushLogFileTimer.start();
|
|
||||||
QObject::connect(&flushLogFileTimer, &QTimer::timeout, []
|
|
||||||
{
|
|
||||||
// qDebug() << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"); // 测试不停的写入内容到日志文件
|
|
||||||
QMutexLocker locker(&LogHandlerPrivate::logMutex);
|
|
||||||
if (nullptr != logOut)
|
|
||||||
{
|
|
||||||
logOut->flush();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
LogHandlerPrivate::~LogHandlerPrivate()
|
|
||||||
{
|
|
||||||
if (nullptr != logFile)
|
|
||||||
{
|
|
||||||
logFile->flush();
|
|
||||||
logFile->close();
|
|
||||||
delete logOut;
|
|
||||||
delete logFile;
|
|
||||||
|
|
||||||
// 因为他们是 static 变量
|
|
||||||
logOut = nullptr;
|
|
||||||
logFile = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开日志文件 log.txt,如果不是当天创建的,则使用创建日期把其重命名为 yyyy-MM-dd.log,并重新创建一个 log.txt
|
|
||||||
void LogHandlerPrivate::openAndBackupLogFile()
|
|
||||||
{
|
|
||||||
// 总体逻辑:
|
|
||||||
// 1. 程序启动时 logFile 为 nullptr,初始化 logFile,有可能是同一天打开已经存在的 logFile,所以使用 Append 模式
|
|
||||||
// 2. logFileCreatedDate is nullptr, 说明日志文件在程序开始时不存在,所以记录下创建时间
|
|
||||||
// 3. 程序运行时检查如果 logFile 的创建日期和当前日期不相等,则使用它的创建日期重命名,然后再生成一个新的 log.txt 文件
|
|
||||||
// 4. 检查日志文件超过 LOGLIMIT_NUM 个,删除最早的
|
|
||||||
// 备注:log.txt 始终为当天的日志文件,当第二天,会执行第3步,将使用 log.txt 的创建日期重命名它
|
|
||||||
|
|
||||||
// 如果日志所在目录不存在,则创建
|
|
||||||
if (!logDir.exists())
|
|
||||||
{
|
|
||||||
logDir.mkpath("."); // 可以递归的创建文件夹
|
|
||||||
}
|
|
||||||
QString logPath = logDir.absoluteFilePath("today.log"); // log.txt的路径
|
|
||||||
|
|
||||||
// [[1]] 程序每次启动时 logFile 为 nullptr
|
|
||||||
if (logFile == nullptr)
|
|
||||||
{
|
|
||||||
logFile = new QFile(logPath);
|
|
||||||
logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) ? new QTextStream(logFile) : nullptr;
|
|
||||||
if (logOut != nullptr)
|
|
||||||
logOut->setCodec("UTF-8");
|
|
||||||
|
|
||||||
// [[2]] 如果文件是第一次创建,则创建日期是无效的,把其设置为当前日期
|
|
||||||
if (logFileCreatedDate.isNull())
|
|
||||||
{
|
|
||||||
logFileCreatedDate = QDate::currentDate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [[3]] 程序运行时如果创建日期不是当前日期,则使用创建日期重命名,并生成一个新的 log.txt
|
|
||||||
if (logFileCreatedDate != QDate::currentDate())
|
|
||||||
{
|
|
||||||
logFile->flush();
|
|
||||||
logFile->close();
|
|
||||||
delete logOut;
|
|
||||||
delete logFile;
|
|
||||||
|
|
||||||
QString newLogPath = logDir.absoluteFilePath(logFileCreatedDate.toString("yyyy-MM-dd.log"));;
|
|
||||||
QFile::copy(logPath, newLogPath); // Bug: 按理说 rename 会更合适,但是 rename 时最后一个文件总是显示不出来,需要 killall Finder 后才出现
|
|
||||||
QFile::remove(logPath); // 删除重新创建,改变创建时间
|
|
||||||
|
|
||||||
// 重新创建 log.txt
|
|
||||||
logFile = new QFile(logPath);
|
|
||||||
logOut = (logFile->open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) ? new QTextStream(logFile) : nullptr;
|
|
||||||
logFileCreatedDate = QDate::currentDate();
|
|
||||||
if (logOut != nullptr)
|
|
||||||
logOut->setCodec("UTF-8");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检测当前日志文件大小
|
|
||||||
void LogHandlerPrivate::checkLogFiles()
|
|
||||||
{
|
|
||||||
// 如果 protocal.log 文件大小超过5M,重新创建一个日志文件,原文件存档为yyyy-MM-dd_hhmmss.log
|
|
||||||
if (logFile->size() > 1024 * g_logLimitSize)
|
|
||||||
{
|
|
||||||
logFile->flush();
|
|
||||||
logFile->close();
|
|
||||||
delete logOut;
|
|
||||||
delete logFile;
|
|
||||||
|
|
||||||
QString logPath = logDir.absoluteFilePath("today.log"); // 日志的路径
|
|
||||||
QString newLogPath = logDir.absoluteFilePath(logFileCreatedDate.toString("yyyy-MM-dd.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 = QDate::currentDate();
|
|
||||||
if (logOut != nullptr)
|
|
||||||
logOut->setCodec("UTF-8");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自动删除30天前的日志
|
|
||||||
void LogHandlerPrivate::autoDeleteLog()
|
|
||||||
{
|
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
|
||||||
|
|
||||||
// 前30天
|
|
||||||
QDateTime dateTime1 = now.addDays(-30);
|
|
||||||
QDateTime dateTime2;
|
|
||||||
|
|
||||||
QString logPath = logDir.absoluteFilePath("today.log"); // 日志的路径
|
|
||||||
QDir dir(logPath);
|
|
||||||
QFileInfoList fileList = dir.entryInfoList();
|
|
||||||
foreach (QFileInfo f, fileList )
|
|
||||||
{
|
|
||||||
// "."和".."跳过
|
|
||||||
if (f.baseName() == "" || f.baseName() == "today" || f.baseName() == "today.log")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
dateTime2 = QDateTime::fromString(f.baseName(), "yyyy-MM-dd");
|
|
||||||
if (dateTime2 < dateTime1) // 只要日志时间小于前30天的时间就删除
|
|
||||||
{
|
|
||||||
dir.remove(f.absoluteFilePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 消息处理函数
|
// 消息处理函数
|
||||||
void LogHandlerPrivate::messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&LogHandlerPrivate::logMutex);
|
g_mutex.lock();
|
||||||
QString level;
|
QString level;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
@@ -198,15 +40,6 @@ void LogHandlerPrivate::messageHandler(QtMsgType type, const QMessageLogContext
|
|||||||
QByteArray localMsg = msg.toLocal8Bit();
|
QByteArray localMsg = msg.toLocal8Bit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// std::cout << std::string(localMsg) << std::endl;
|
|
||||||
// qSetMessagePattern("%{appname} %{type} %{time [yyyy-MM-dd hh:mm:ss]} %{file} %{line} %{function} %{message}");
|
|
||||||
|
|
||||||
|
|
||||||
if (nullptr == LogHandlerPrivate::logOut)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 输出到日志文件, 格式: 时间 - [Level] (文件名:行数, 函数): 消息
|
// 输出到日志文件, 格式: 时间 - [Level] (文件名:行数, 函数): 消息
|
||||||
QString fileName = context.file;
|
QString fileName = context.file;
|
||||||
int index = fileName.lastIndexOf(QDir::separator());
|
int index = fileName.lastIndexOf(QDir::separator());
|
||||||
@@ -215,31 +48,43 @@ void LogHandlerPrivate::messageHandler(QtMsgType type, const QMessageLogContext
|
|||||||
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(level)
|
.arg(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")).arg(level)
|
||||||
.arg(fileName).arg(context.line).arg(context.function).arg(msg);
|
.arg(fileName).arg(context.line).arg(context.function).arg(msg);
|
||||||
std::cout << msgOut.toLocal8Bit().constData() << std::endl;
|
std::cout << msgOut.toLocal8Bit().constData() << std::endl;
|
||||||
(*LogHandlerPrivate::logOut) << msgOut << "\n";
|
QTextStream in(&g_file);
|
||||||
}
|
in << msgOut << "\n";
|
||||||
|
// g_file.flush();
|
||||||
LogHandler::LogHandler() : d(nullptr)
|
g_mutex.unlock();
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 给Qt安装消息处理函数
|
// 给Qt安装消息处理函数
|
||||||
void LogHandler::installMessageHandler()
|
void LogHandler::installMessageHandler()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&LogHandlerPrivate::logMutex); // 类似C++11的lock_guard,析构时自动解锁
|
//获取日期
|
||||||
|
auto _currentDate = QDateTime::currentDateTime().toString("yyyy-MM-dd");
|
||||||
|
|
||||||
if (nullptr == d)
|
//打开/创建文件
|
||||||
|
auto _appDirPath = QCoreApplication::applicationDirPath();
|
||||||
|
auto _logDirPath = _appDirPath + "/logs/";
|
||||||
|
QDir _logDir(_logDirPath);
|
||||||
|
if(!_logDir.exists())
|
||||||
|
_logDir.mkpath(_logDirPath);
|
||||||
|
auto _logPath = _logDirPath + _currentDate + ".log";
|
||||||
|
g_file.setFileName(_logPath);
|
||||||
|
if(!g_file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append))
|
||||||
{
|
{
|
||||||
d = new LogHandlerPrivate();
|
return;
|
||||||
qInstallMessageHandler(LogHandlerPrivate::messageHandler); // 给 Qt 安装自定义消息处理函数
|
|
||||||
}
|
}
|
||||||
|
// g_in = new QTextStream(&g_file);
|
||||||
|
qInstallMessageHandler(messageHandler); // 给 Qt 安装自定义消息处理函数
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消安装消息处理函数并释放资源
|
// 取消安装消息处理函数并释放资源
|
||||||
void LogHandler::uninstallMessageHandler()
|
void LogHandler::uninstallMessageHandler()
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&LogHandlerPrivate::logMutex);
|
g_mutex.lock();
|
||||||
|
if(g_file.isOpen())
|
||||||
|
{
|
||||||
|
g_file.flush();
|
||||||
|
g_file.close();
|
||||||
|
}
|
||||||
|
g_mutex.unlock();
|
||||||
qInstallMessageHandler(nullptr);
|
qInstallMessageHandler(nullptr);
|
||||||
delete d;
|
|
||||||
d = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-35
@@ -13,43 +13,11 @@
|
|||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include "Singleton.h"
|
#include "Singleton.h"
|
||||||
|
|
||||||
//#define log_d(format,...) qDebug(format,__VA_ARGS__)
|
|
||||||
//#define log_i(format,...) qInfo(format,__VA_ARGS__)
|
|
||||||
//#define log_w(format,...) qWarning(format,__VA_ARGS__)
|
|
||||||
//#define log_e(format,...) qCritical(format,__VA_ARGS__)
|
|
||||||
//#define log_f(format,...) qFatal(format,__VA_ARGS__)
|
|
||||||
|
|
||||||
const int g_logLimitSize = 5;
|
|
||||||
|
|
||||||
struct LogHandlerPrivate
|
|
||||||
{
|
|
||||||
LogHandlerPrivate();
|
|
||||||
~LogHandlerPrivate();
|
|
||||||
// qDebug()
|
|
||||||
// 打开日志文件 log.txt,如果日志文件不是当天创建的,则使用创建日期把其重命名为 yyyy-MM-dd.log,并重新创建一个 log.txt
|
|
||||||
void openAndBackupLogFile();
|
|
||||||
void checkLogFiles(); // 检测当前日志文件大小
|
|
||||||
void autoDeleteLog(); // 自动删除30天前的日志
|
|
||||||
|
|
||||||
// 消息处理函数
|
|
||||||
static void messageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg);
|
|
||||||
|
|
||||||
QDir logDir; // 日志文件夹
|
|
||||||
QTimer renameLogFileTimer; // 重命名日志文件使用的定时器
|
|
||||||
QTimer flushLogFileTimer; // 刷新输出到日志文件的定时器
|
|
||||||
QDate logFileCreatedDate; // 日志文件创建的时间
|
|
||||||
|
|
||||||
static QFile *logFile; // 日志文件
|
|
||||||
static QTextStream *logOut; // 输出日志的 QTextStream,使用静态对象就是为了减少函数调用的开销
|
|
||||||
static QMutex logMutex; // 同步使用的 mutex
|
|
||||||
};
|
|
||||||
|
|
||||||
class LogHandler
|
class LogHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void installMessageHandler(); // 给Qt安装消息处理函数
|
void installMessageHandler(); // 给Qt安装消息处理函数
|
||||||
void uninstallMessageHandler(); // 取消安装消息处理函数并释放资源
|
void uninstallMessageHandler(); // 取消安装消息处理函数并释放资源
|
||||||
|
|
||||||
static LogHandler& Get()
|
static LogHandler& Get()
|
||||||
{
|
{
|
||||||
static LogHandler m_logHandler;
|
static LogHandler m_logHandler;
|
||||||
@@ -57,9 +25,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LogHandler();
|
LogHandler(){};
|
||||||
|
|
||||||
LogHandlerPrivate *d;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LOGHANDLER_H
|
#endif // LOGHANDLER_H
|
||||||
|
|||||||
Reference in New Issue
Block a user