using System.Text; using System.Text.Json; using System.IO; namespace AxCopilot.Services; public static class AgentPerformanceLogService { private static readonly string PerfDir; private static readonly object _lock = new(); private static readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = false, Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping, }; static AgentPerformanceLogService() { PerfDir = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AxCopilot", "perf"); try { Directory.CreateDirectory(PerfDir); } catch { } } public static void LogMetric( string area, string name, string conversationId, string tab, long durationMs, object? detail = null) { try { var fileName = $"performance-{DateTime.Now:yyyy-MM-dd}.json"; var filePath = Path.Combine(PerfDir, fileName); var json = JsonSerializer.Serialize(new AgentPerformanceEntry { Timestamp = DateTime.Now, Area = area, Name = name, ConversationId = conversationId ?? "", Tab = tab ?? "", DurationMs = durationMs, Detail = detail == null ? "" : JsonSerializer.Serialize(detail, _jsonOptions), }, _jsonOptions); lock (_lock) { File.AppendAllText(filePath, json + Environment.NewLine, Encoding.UTF8); } } catch { // 성능 로그 실패는 런타임에 영향 주지 않음 } } public static string GetPerformanceFolder() => PerfDir; } public sealed class AgentPerformanceEntry { public DateTime Timestamp { get; init; } public string Area { get; init; } = ""; public string Name { get; init; } = ""; public string ConversationId { get; init; } = ""; public string Tab { get; init; } = ""; public long DurationMs { get; init; } public string Detail { get; init; } = ""; }