diff --git a/loghandler.cpp b/loghandler.cpp index 4f8532b..3825731 100644 --- a/loghandler.cpp +++ b/loghandler.cpp @@ -47,6 +47,8 @@ struct LogState QDate activeDate; QString currentFilePrefix; bool installed = false; + std::function updateCallback; // 回调仅投递通知,不在锁内访问界面或日志。 + QStringList recentLines; // 有界缓存最近打印内容;后台线程与界面均通过 mutex 访问。 #if defined(MYLOGGER_TESTING) // 测试编译时将文件系统副作用收敛到临时目录,并用小阈值快速覆盖分卷逻辑。 QString testLogDirectory; @@ -313,6 +315,13 @@ void messageHandler(QtMsgType type, const QMessageLogContext &context, const QSt 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(); @@ -416,6 +425,23 @@ bool LogHandler::isInstalled() const return g_logState.installed; } +// 回调的安装与清除同样持锁,确保界面销毁后后台线程不再访问接收对象。 +void LogHandler::setUpdateCallback(const std::function &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) // 测试目录设置在同一把锁下更新,并要求 handler 已卸载,保证没有线程持有旧目录的文件句柄。 void LogHandlerTestAccess::setLogDirectory(const QString &directory) diff --git a/loghandler.h b/loghandler.h index c6f9fb8..16fccfa 100644 --- a/loghandler.h +++ b/loghandler.h @@ -2,6 +2,9 @@ #define LOGHANDLER_H #include +#include +#include +#include #if defined(MYLOGGER_TESTING) #include @@ -31,6 +34,12 @@ public: // 返回本库是否仍持有并安装了 Qt 全局消息处理器,供宿主避免重复管理生命周期。 bool isInstalled() const; + // 在同一把锁下读取当前文件、大小和最近日志,供界面安全轮询。 + void snapshot(bool &active, QString &path, qint64 &bytes, QStringList &lines) const; + + // 设置轻量日志通知;回调在写入线程执行,不能访问日志处理器或界面。 + void setUpdateCallback(const std::function &callback); + // 返回进程内唯一实例;C++11 局部静态对象保证首次构造线程安全。 static LogHandler& Get() {