feat: complete TASK-WP0-08 logging

This commit is contained in:
suyx
2026-07-28 00:11:02 +08:00
parent 096a1f1279
commit 361bda2f22
21 changed files with 1050 additions and 22 deletions
@@ -32,6 +32,7 @@ internal static class Program
{
var security = await TestCredentialBoundaryAsync();
var supervisor = await TestSupervisorLifecycleAsync();
TestStructuredLogging();
WriteEvidence(Environment.GetEnvironmentVariable("DADA_EVIDENCE_DIR_SEC"), security);
WriteEvidence(Environment.GetEnvironmentVariable("DADA_EVIDENCE_DIR_SUP"), supervisor);
Console.WriteLine(JsonSerializer.Serialize(new { security = "passed", supervisor = "passed" }, JsonOptions));
@@ -44,6 +45,35 @@ internal static class Program
}
}
private static void TestStructuredLogging()
{
Equal(10 * 1024 * 1024L, StructuredJsonlLogger.SizeLimitBytes, "Supervisor log byte limit");
Equal(10, StructuredJsonlLogger.FileLimit, "Supervisor log file limit");
Equal(30, StructuredJsonlLogger.RetentionDays, "Supervisor log retention");
var directory = Path.Combine(Path.GetTempPath(), $"dada-supervisor-log-{Guid.NewGuid():N}");
try
{
var logger = new StructuredJsonlLogger(directory, "supervisor", sizeLimitBytes: 512, fileLimit: 10);
for (var index = 0; index < 40; index++)
{
logger.Write(new StructuredLogEvent("completed", $"corr_{index}", $"obj_{index}", index, "none"));
}
var files = Directory.GetFiles(directory, "*.jsonl");
True(files.Length <= StructuredJsonlLogger.FileLimit, "Supervisor log file cap");
True(files.All(path => new FileInfo(path).Length <= 512), "Supervisor log rotation byte boundary");
foreach (var line in files.SelectMany(File.ReadLines))
{
using var document = JsonDocument.Parse(line);
var keys = document.RootElement.EnumerateObject().Select(property => property.Name).ToHashSet(StringComparer.Ordinal);
True(keys.IsSubsetOf(["schema_version", "timestamp", "component", "status_category", "correlation_id", "object_id", "duration_ms", "error_category"]), "Supervisor log field allowlist");
}
}
finally
{
if (Directory.Exists(directory)) Directory.Delete(directory, recursive: true);
}
}
private static async Task<object> TestCredentialBoundaryAsync()
{
var store = new TestCredentialStore();
@@ -184,6 +214,11 @@ internal static class Program
{
EqualSequence(new[] { "打开 Dada", "运行状态", "打开诊断", "重新启动服务", "退出 Dada" }, menuForm.TrayMenuLabels, "tray menu order");
}
using (var diagnostics = new DiagnosticsForm(SupervisorState.StorageUnavailable))
{
True(diagnostics.CopyPayload.Contains("log_write_failed", StringComparison.Ordinal), "diagnostic log status");
False(diagnostics.CopyPayload.Contains("CredentialBlob", StringComparison.Ordinal), "diagnostic credential redaction");
}
var screenshotPath = Path.Combine(Environment.GetEnvironmentVariable("DADA_EVIDENCE_DIR_SUP") ?? Path.GetTempPath(), "screenshots", "system-ui.png");
Directory.CreateDirectory(Path.GetDirectoryName(screenshotPath)!);