using System.Text.Json; using System.Text.Json.Serialization; namespace Sdt.Core; public sealed class RunEventJsonlRecorder : IDisposable { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false }; static RunEventJsonlRecorder() { JsonOptions.Converters.Add(new JsonStringEnumConverter()); } private readonly StreamWriter _writer; private readonly object _gate = new(); private bool _disposed; public string FilePath { get; } private RunEventJsonlRecorder(string filePath, StreamWriter writer) { FilePath = filePath; _writer = writer; } public static RunEventJsonlRecorder Create(string projectRoot, string category) { var root = Path.Combine(projectRoot, ".sdt", "events"); Directory.CreateDirectory(root); var fileName = $"{category}-{DateTimeOffset.Now:yyyyMMdd-HHmmss}.jsonl"; var path = Path.Combine(root, fileName); var writer = new StreamWriter(new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) { AutoFlush = true }; return new RunEventJsonlRecorder(path, writer); } public void Write(RunEvent evt) { lock (_gate) { if (_disposed) return; var line = JsonSerializer.Serialize(evt, JsonOptions); _writer.WriteLine(line); } } public void Dispose() { lock (_gate) { if (_disposed) return; _disposed = true; _writer.Dispose(); } } }