在 MiGu 侧落地日志定时清理与磁盘空间告警。

系统配置可开关清理策略;日志管理页支持查看/保存清理选项。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
黄兆尉
2026-08-26 17:48:25 +08:00
co-authored by Cursor
parent 4180140ae4
commit 79c9e36d89
12 changed files with 856 additions and 150 deletions
@@ -3,7 +3,7 @@ import http from './http'
/**
* 「日志管理」前端胶水:对应 MiGu.Server `LogsController``/api/logs/*`)。
*
* 数据是 SimpleLite 内核 `Diagnosis.Post / Diagnosis.Log` 写到工作目录 `log/{日期}/xxx.log` 的落盘日志
* 数据是 Simple3 内核 `Diagnosis.Post / Diagnosis.Log` 写到工作目录 `log/{日期}/xxx.log` 的落盘日志
* (俗称 DLog)。后端直接读文件并解析为结构化条目,提供:概览 / 文件列表 / 条目分页 /
* 「按标签合订」/ 原文 / 下载。
*
@@ -14,10 +14,10 @@ import http from './http'
const MOCK = import.meta.env.VITE_USE_MOCK === 'true'
const API_BASE = (import.meta.env.VITE_API_BASE as string | undefined) ?? '/api'
/** SimpleLite projection 投影 API 统一信封(同 reflection.ts)。 */
/** Simple3 projection 投影 API 统一信封(同 reflection.ts)。 */
interface SlEnvelope<T> { success: boolean; code: number; data: T | null; message: string }
/** 经 YARP 反代到 SimpleLite EmbedIO 的诊断投影端点(/api/sl/projection/diagnosis)。 */
/** 经 YARP 反代到 Simple3 EmbedIO 的诊断投影端点(/api/sl/projection/diagnosis)。 */
const SL_DIAG = '/sl/projection/diagnosis'
export interface LogEntry {
@@ -47,6 +47,31 @@ export interface LogDayStat {
bytes: number
}
export interface LogDiskStatus {
known: boolean
drive: string
freeGB: number
alertEnabled: boolean
alertGB: number
belowThreshold: boolean
}
export interface LogCleanupConfig {
enabled: boolean
retentionDays: number
checkIntervalHours: number
runOnStartup: boolean
diskAlertEnabled: boolean
diskFreeAlertGB: number
diskCheckIntervalMinutes: number
}
export interface LogCleanupResult {
deletedFiles: number
freedBytes: number
freedMB: number
}
export interface LogOverview {
exists: boolean
root: string | null
@@ -56,6 +81,7 @@ export interface LogOverview {
latestFileTime?: string | null
days?: LogDayStat[]
message?: string
disk?: LogDiskStatus
}
export interface LogFilesResult {
@@ -116,7 +142,7 @@ export interface DigestQuery {
maxPerTag?: number
}
/** 实时诊断单条:对应 SimpleLite 内核 Diagnosis 的内存态 Post/Toast。 */
/** 实时诊断单条:对应 Simple3 内核 Diagnosis 的内存态 Post/Toast。 */
export interface LiveDiagItem {
index: number
time: string
@@ -225,12 +251,25 @@ export interface AnalyzeQuery {
function mockOverview(): LogOverview {
return {
exists: true,
root: 'E:\\...\\SimpleLite\\bin\\Debug\\log',
workingDirectory: 'E:\\...\\SimpleLite\\bin\\Debug',
root: 'E:\\...\\Simple3\\bin\\Debug\\log',
workingDirectory: 'E:\\...\\Simple3\\bin\\Debug',
totalFiles: 3,
totalBytes: 5_233_649,
latestFileTime: new Date().toISOString(),
days: [{ day: '2026-06-01', files: 3, bytes: 5_233_649 }]
days: [{ day: '2026-06-01', files: 3, bytes: 5_233_649 }],
disk: { known: true, drive: 'E:\\', freeGB: 118.7, alertEnabled: true, alertGB: 5, belowThreshold: false }
}
}
function mockCleanupConfig(): LogCleanupConfig {
return {
enabled: true,
retentionDays: 30,
checkIntervalHours: 24,
runOnStartup: true,
diskAlertEnabled: true,
diskFreeAlertGB: 5,
diskCheckIntervalMinutes: 30
}
}
@@ -285,7 +324,7 @@ function mockLiveDiagnosis(): LiveDiagnosis {
{ index: 0, time: iso(2), tag: 'Persistence', tagged: true, content: 'flush 4 entities ok' },
{ index: 1, time: iso(4), tag: 'Dispatch', tagged: true, content: 'dispatch loop 50Hz online' },
{ index: 2, time: iso(6), tag: 'InternalAuth', tagged: true, content: '仅放行本机回环(无 internal token 配置)' },
{ index: 3, time: iso(1), tag: '', tagged: false, content: '[SimpleLite] Projection API listening on http://127.0.0.1:8222/projection/' },
{ index: 3, time: iso(1), tag: '', tagged: false, content: '[Simple3] Projection API listening on http://127.0.0.1:8222/projection/' },
{ index: 4, time: iso(8), tag: '', tagged: false, content: 'loaded 12 sites, 18 tracks' }
]
const taggedCount = items.filter((i) => i.tagged).length
@@ -373,7 +412,7 @@ export const logsApi = {
? Promise.resolve(mockDigest())
: http.get<LogDigest>('/logs/digest', { params: q }).then((r) => r.data),
/** 实时内存诊断(SimpleLite Diagnosis.GetAllDiagnosis,经 YARP 反代)。需 SimpleLite 在运行,否则 502。 */
/** 实时内存诊断(Simple3 Diagnosis.GetAllDiagnosis,经 YARP 反代)。需 Simple3 在运行,否则 502。 */
liveDiagnosis: (): Promise<LiveDiagnosis> => MOCK
? Promise.resolve(mockLiveDiagnosis())
: http.get<SlEnvelope<LiveDiagnosis>>(`${SL_DIAG}/all`).then((r) => {
@@ -398,5 +437,17 @@ export const logsApi = {
/** 浏览器直链下载(GET,靠 httpOnly Cookie 鉴权)。 */
downloadUrl: (file: string): string =>
`${API_BASE}/logs/download?file=${encodeURIComponent(file)}`
`${API_BASE}/logs/download?file=${encodeURIComponent(file)}`,
cleanupConfig: (): Promise<LogCleanupConfig> => MOCK
? Promise.resolve(mockCleanupConfig())
: http.get<LogCleanupConfig>('/logs/cleanup-config').then((r) => r.data),
saveCleanupConfig: (body: LogCleanupConfig): Promise<LogCleanupConfig> => MOCK
? Promise.resolve({ ...body })
: http.put<LogCleanupConfig>('/logs/cleanup-config', body).then((r) => r.data),
cleanupNow: (): Promise<LogCleanupResult> => MOCK
? Promise.resolve({ deletedFiles: 0, freedBytes: 0, freedMB: 0 })
: http.post<LogCleanupResult>('/logs/cleanup-now').then((r) => r.data)
}