feat: 提供实时日志快照
This commit is contained in:
@@ -47,6 +47,8 @@ struct LogState
|
|||||||
QDate activeDate;
|
QDate activeDate;
|
||||||
QString currentFilePrefix;
|
QString currentFilePrefix;
|
||||||
bool installed = false;
|
bool installed = false;
|
||||||
|
std::function<void()> updateCallback; // 回调仅投递通知,不在锁内访问界面或日志。
|
||||||
|
QStringList recentLines; // 有界缓存最近打印内容;后台线程与界面均通过 mutex 访问。
|
||||||
#if defined(MYLOGGER_TESTING)
|
#if defined(MYLOGGER_TESTING)
|
||||||
// 测试编译时将文件系统副作用收敛到临时目录,并用小阈值快速覆盖分卷逻辑。
|
// 测试编译时将文件系统副作用收敛到临时目录,并用小阈值快速覆盖分卷逻辑。
|
||||||
QString testLogDirectory;
|
QString testLogDirectory;
|
||||||
@@ -313,6 +315,13 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 只缓存最近打印,避免界面长期运行时无限增长;与文件写入共用锁。
|
||||||
|
g_logState.recentLines.append(timestamp + QStringLiteral(" ") + msg);
|
||||||
|
if (g_logState.recentLines.size() > 1000)
|
||||||
|
g_logState.recentLines.removeFirst();
|
||||||
|
if (g_logState.updateCallback)
|
||||||
|
g_logState.updateCallback();
|
||||||
|
|
||||||
// 跨午夜时先关闭旧日期文件再按新日期选择分卷;轮转失败后的后续消息也会重试打开,
|
// 跨午夜时先关闭旧日期文件再按新日期选择分卷;轮转失败后的后续消息也会重试打开,
|
||||||
// 这样临时磁盘或权限故障恢复后无需重启应用即可恢复文件日志。
|
// 这样临时磁盘或权限故障恢复后无需重启应用即可恢复文件日志。
|
||||||
const QDate today = QDate::currentDate();
|
const QDate today = QDate::currentDate();
|
||||||
@@ -416,6 +425,23 @@ bool LogHandler::isInstalled() const
|
|||||||
return g_logState.installed;
|
return g_logState.installed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 回调的安装与清除同样持锁,确保界面销毁后后台线程不再访问接收对象。
|
||||||
|
void LogHandler::setUpdateCallback(const std::function<void()> &callback)
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&g_logState.mutex);
|
||||||
|
g_logState.updateCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 快照不暴露文件句柄,后台写入和界面轮询不会并发访问 QTextStream。
|
||||||
|
void LogHandler::snapshot(bool &active, QString &path, qint64 &bytes, QStringList &lines) const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&g_logState.mutex);
|
||||||
|
active = g_logState.installed && g_logState.file.isOpen();
|
||||||
|
path = active ? g_logState.file.fileName() : logDirectoryPath();
|
||||||
|
bytes = active ? g_logState.currentFileBytes : 0;
|
||||||
|
lines = g_logState.recentLines;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(MYLOGGER_TESTING)
|
#if defined(MYLOGGER_TESTING)
|
||||||
// 测试目录设置在同一把锁下更新,并要求 handler 已卸载,保证没有线程持有旧目录的文件句柄。
|
// 测试目录设置在同一把锁下更新,并要求 handler 已卸载,保证没有线程持有旧目录的文件句柄。
|
||||||
void LogHandlerTestAccess::setLogDirectory(const QString &directory)
|
void LogHandlerTestAccess::setLogDirectory(const QString &directory)
|
||||||
|
|||||||
@@ -2,6 +2,9 @@
|
|||||||
#define LOGHANDLER_H
|
#define LOGHANDLER_H
|
||||||
|
|
||||||
#include <QtCore/qglobal.h>
|
#include <QtCore/qglobal.h>
|
||||||
|
#include <QString>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#if defined(MYLOGGER_TESTING)
|
#if defined(MYLOGGER_TESTING)
|
||||||
#include <QString>
|
#include <QString>
|
||||||
@@ -31,6 +34,12 @@ public:
|
|||||||
// 返回本库是否仍持有并安装了 Qt 全局消息处理器,供宿主避免重复管理生命周期。
|
// 返回本库是否仍持有并安装了 Qt 全局消息处理器,供宿主避免重复管理生命周期。
|
||||||
bool isInstalled() const;
|
bool isInstalled() const;
|
||||||
|
|
||||||
|
// 在同一把锁下读取当前文件、大小和最近日志,供界面安全轮询。
|
||||||
|
void snapshot(bool &active, QString &path, qint64 &bytes, QStringList &lines) const;
|
||||||
|
|
||||||
|
// 设置轻量日志通知;回调在写入线程执行,不能访问日志处理器或界面。
|
||||||
|
void setUpdateCallback(const std::function<void()> &callback);
|
||||||
|
|
||||||
// 返回进程内唯一实例;C++11 局部静态对象保证首次构造线程安全。
|
// 返回进程内唯一实例;C++11 局部静态对象保证首次构造线程安全。
|
||||||
static LogHandler& Get()
|
static LogHandler& Get()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user