在 MiGu 侧落地日志定时清理与磁盘空间告警。
系统配置可开关清理策略;日志管理页支持查看/保存清理选项。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MiGu.Server.Configs;
|
||||
using MiGu.Server.Logs;
|
||||
|
||||
namespace MiGu.Server.Controllers;
|
||||
|
||||
@@ -13,10 +14,12 @@ namespace MiGu.Server.Controllers;
|
||||
public class ConfigController : ControllerBase
|
||||
{
|
||||
private readonly ConfigStore _store;
|
||||
private readonly LogCleanupService _cleanup;
|
||||
|
||||
public ConfigController(ConfigStore store)
|
||||
public ConfigController(ConfigStore store, LogCleanupService cleanup)
|
||||
{
|
||||
_store = store;
|
||||
_cleanup = cleanup;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
@@ -66,6 +69,8 @@ public class ConfigController : ControllerBase
|
||||
}
|
||||
|
||||
var env = _store.Put(section, payload);
|
||||
if (string.Equals(section, "system", StringComparison.OrdinalIgnoreCase))
|
||||
_cleanup.ApplySchedule();
|
||||
return Ok(new
|
||||
{
|
||||
section = env.Section,
|
||||
|
||||
@@ -2,18 +2,20 @@ using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using MiGu.Server.Configs;
|
||||
using MiGu.Server.Launcher;
|
||||
using MiGu.Server.Logs;
|
||||
|
||||
namespace MiGu.Server.Controllers;
|
||||
|
||||
/// <summary>
|
||||
/// 平台「日志管理」后端:把 SimpleLite 内核 <c>SimpleCore.Library.Diagnosis</c> 的落盘日志
|
||||
/// 平台「日志管理」后端:把 Simple3 内核 <c>SimpleCore.Library.Diagnosis</c> 的落盘日志
|
||||
/// (<c>Diagnosis.Post</c> / <c>Diagnosis.Log</c> 写入的 <c>log/**/*.log</c>,俗称 DLog)暴露给
|
||||
/// platform-vue 的配置中心「日志管理」页查看。对齐 SimpleLite 桌面端 <c>SimpleLite/UI/LogViewer.cs</c>
|
||||
/// platform-vue 的配置中心「日志管理」页查看。对齐 Simple3 桌面端 <c>Simple3/UI/LogViewer.cs</c>
|
||||
/// 的两区设计(诊断条目表 + 落盘文件表),并在 Web 端额外提供「按标签合订」视图。
|
||||
///
|
||||
/// 数据来源:日志是 SimpleLite 进程在其工作目录写出的历史文件,<b>不依赖 SimpleLite 是否在运行</b>,
|
||||
/// MiGu.Server 通过 <see cref="SimpleLiteLauncher.ResolveWorkingDirectory"/> 定位工作目录后直接读
|
||||
/// 数据来源:日志是 Simple3 进程在其工作目录写出的历史文件,<b>不依赖 Simple3 是否在运行</b>,
|
||||
/// MiGu.Server 通过 <see cref="Simple3Launcher.ResolveWorkingDirectory"/> 定位工作目录后直接读
|
||||
/// <c>{工作目录}/log/</c>。可用 appsettings <c>Logs:Root</c> 显式覆盖日志根目录。
|
||||
///
|
||||
/// 落盘行格式(见 Diagnosis.Log):<c>[{prefix}yyyy/MM/dd-HH:mm:ss.fff] >{tag}: {content}</c>,
|
||||
@@ -59,15 +61,21 @@ public sealed class LogsController : ControllerBase
|
||||
private static readonly Regex NumericFieldRegex =
|
||||
new(@"(?<k>[A-Za-z_\u4e00-\u9fff][\w\u4e00-\u9fff\.]*)\s*[=:]\s*(?<v>-?\d+(?:\.\d+)?)", RegexOptions.Compiled);
|
||||
|
||||
private readonly SimpleLiteLauncher _launcher;
|
||||
private readonly Simple3Launcher _launcher;
|
||||
private readonly IConfiguration _config;
|
||||
private readonly ILogger<LogsController> _log;
|
||||
private readonly LogCleanupService _cleanup;
|
||||
|
||||
public LogsController(SimpleLiteLauncher launcher, IConfiguration config, ILogger<LogsController> log)
|
||||
public LogsController(
|
||||
Simple3Launcher launcher,
|
||||
IConfiguration config,
|
||||
ILogger<LogsController> log,
|
||||
LogCleanupService cleanup)
|
||||
{
|
||||
_launcher = launcher;
|
||||
_config = config;
|
||||
_log = log;
|
||||
_cleanup = cleanup;
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────── 概览 ──
|
||||
@@ -78,14 +86,15 @@ public sealed class LogsController : ControllerBase
|
||||
{
|
||||
var (root, workdir, error) = ResolveLogRoot();
|
||||
if (root == null)
|
||||
return Ok(new { exists = false, root = (string?)null, workingDirectory = workdir, message = error });
|
||||
return Ok(new { exists = false, root = (string?)null, workingDirectory = workdir, message = error, disk = DiskDto() });
|
||||
|
||||
if (!Directory.Exists(root))
|
||||
return Ok(new
|
||||
{
|
||||
exists = false, root, workingDirectory = workdir,
|
||||
totalFiles = 0, totalBytes = 0L, days = Array.Empty<object>(),
|
||||
message = "日志目录尚未生成(SimpleLite 产生落盘日志后会自动创建 log 目录)。"
|
||||
message = "日志目录尚未生成(Simple3 产生落盘日志后会自动创建 log 目录)。",
|
||||
disk = DiskDto()
|
||||
});
|
||||
|
||||
var files = EnumerateLogFiles(root);
|
||||
@@ -101,7 +110,32 @@ public sealed class LogsController : ControllerBase
|
||||
totalFiles = files.Count,
|
||||
totalBytes = files.Sum(f => f.Bytes),
|
||||
latestFileTime = files.Count > 0 ? files.Max(f => f.Mtime) : (DateTime?)null,
|
||||
days
|
||||
days,
|
||||
disk = DiskDto()
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>日志清理配置(对齐 Simple3 logCleanup)。</summary>
|
||||
[HttpGet("cleanup-config")]
|
||||
public IActionResult GetCleanupConfig() => Ok(_cleanup.GetOptions());
|
||||
|
||||
/// <summary>保存日志清理配置并立即重排后台定时器。</summary>
|
||||
[HttpPut("cleanup-config")]
|
||||
public IActionResult PutCleanupConfig([FromBody] LogCleanupOptions body)
|
||||
=> Ok(_cleanup.SaveOptions(body ?? new LogCleanupOptions()));
|
||||
|
||||
/// <summary>按当前保留天数立即清理一次过期 *.log。</summary>
|
||||
[HttpPost("cleanup-now")]
|
||||
public IActionResult CleanupNow()
|
||||
{
|
||||
var r = _cleanup.CleanNow();
|
||||
if (r.Error != null)
|
||||
return Problem(r.Error, statusCode: 500);
|
||||
return Ok(new
|
||||
{
|
||||
deletedFiles = r.DeletedFiles,
|
||||
freedBytes = r.FreedBytes,
|
||||
freedMB = Math.Round(r.FreedMB, 1)
|
||||
});
|
||||
}
|
||||
|
||||
@@ -151,7 +185,7 @@ public sealed class LogsController : ControllerBase
|
||||
root = normRoot, exists = false, path = "", parent = (string?)null,
|
||||
dirCount = 0, fileCount = 0,
|
||||
dirs = Array.Empty<object>(), files = Array.Empty<object>(),
|
||||
message = "日志目录尚未生成(SimpleLite 产生落盘日志后会自动创建 log 目录)。"
|
||||
message = "日志目录尚未生成(Simple3 产生落盘日志后会自动创建 log 目录)。"
|
||||
});
|
||||
|
||||
var target = SafeResolveDir(normRoot, path);
|
||||
@@ -592,9 +626,23 @@ public sealed class LogsController : ControllerBase
|
||||
return PhysicalFile(full, "application/octet-stream", downloadName);
|
||||
}
|
||||
|
||||
private object DiskDto()
|
||||
{
|
||||
var d = _cleanup.GetDiskStatus();
|
||||
return new
|
||||
{
|
||||
known = d.Known,
|
||||
drive = d.Drive,
|
||||
freeGB = Math.Round(d.FreeGB, 1),
|
||||
alertEnabled = d.AlertEnabled,
|
||||
alertGB = d.AlertGB,
|
||||
belowThreshold = d.BelowThreshold
|
||||
};
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────── helpers ──
|
||||
|
||||
/// <summary>定位日志根:优先 appsettings <c>Logs:Root</c>,否则取 SimpleLite 工作目录下的 <c>log</c>。</summary>
|
||||
/// <summary>定位日志根:优先 appsettings <c>Logs:Root</c>,否则取 Simple3 工作目录下的 <c>log</c>。</summary>
|
||||
private (string? root, string? workdir, string? error) ResolveLogRoot()
|
||||
{
|
||||
var overrideRoot = _config["Logs:Root"];
|
||||
@@ -607,7 +655,7 @@ public sealed class LogsController : ControllerBase
|
||||
var wd = _launcher.ResolveWorkingDirectory();
|
||||
if (string.IsNullOrWhiteSpace(wd))
|
||||
return (null, null,
|
||||
"未能定位 SimpleLite 工作目录,无法读取日志。请在 appsettings.json 配置 SimpleLite:WorkingDirectory," +
|
||||
"未能定位 Simple3 工作目录,无法读取日志。请在 appsettings.json 配置 Simple3:WorkingDirectory," +
|
||||
"或显式设置 Logs:Root 指向日志根目录。");
|
||||
|
||||
return (Path.GetFullPath(Path.Combine(wd, "log")), wd, null);
|
||||
|
||||
Reference in New Issue
Block a user