using System.Diagnostics; using System.Text.Json; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.ProcessPower.PlantInstance; namespace PRecAnnot.Plant3D2026; internal static class RuntimeDiagnostics { private static readonly object Gate = new(); private static readonly string LogDirectory = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Promhimproekt", "PRecAnnot2026", "Logs"); public static string RuntimeMode => Environment.GetEnvironmentVariable("P_REC_ANNOT_RUNTIME_MODE") ?? "Normal"; public static bool IsOrthoStructureDiagnosticsEnabled => string.Equals(RuntimeMode, "DryRun", StringComparison.OrdinalIgnoreCase) && string.Equals(Environment.GetEnvironmentVariable("P_REC_ANNOT_DIAGNOSTICS"), "OrthoStructure", StringComparison.OrdinalIgnoreCase); public static void Write( string eventType, string level = "INFO", string? operationId = null, string? commandName = null, string? resultStatus = null, System.Exception? exception = null, Document? document = null, IReadOnlyDictionary? details = null) { try { var payload = new Dictionary(StringComparer.Ordinal) { ["Timestamp"] = DateTimeOffset.UtcNow, ["Level"] = level, ["OperationId"] = operationId, ["EventType"] = eventType, ["AssemblyVersion"] = typeof(EntryPoint).Assembly.GetName().Version?.ToString(), ["AutoCADVersion"] = typeof(Document).Assembly.GetName().Version?.ToString(), ["PlantVersion"] = typeof(PlantApplication).Assembly.GetName().Version?.ToString(), ["ProcessId"] = Environment.ProcessId, ["ThreadId"] = Environment.CurrentManagedThreadId, ["ApartmentState"] = Thread.CurrentThread.GetApartmentState().ToString(), ["ActiveDocumentName"] = SafeDocumentName(document), ["CommandName"] = commandName, ["ResultStatus"] = resultStatus, ["ExceptionType"] = exception?.GetType().FullName, ["ExceptionMessage"] = exception?.Message, ["StackTrace"] = level.Equals("ERROR", StringComparison.OrdinalIgnoreCase) ? exception?.StackTrace : null, ["RuntimeMode"] = RuntimeMode }; if (details is not null) foreach (var item in details) payload[item.Key] = item.Value; var line = JsonSerializer.Serialize(payload) + Environment.NewLine; lock (Gate) { Directory.CreateDirectory(LogDirectory); File.AppendAllText(Path.Combine(LogDirectory, "runtime.jsonl"), line); } } catch { // Runtime diagnostics must never prevent AutoCAD command execution. } } private static string? SafeDocumentName(Document? document) { try { return document is null ? null : Path.GetFileName(document.Name); } catch { return null; } } } internal sealed class SmokeWriteBlockedException() : InvalidOperationException("SMOKE_WRITE_BLOCKED"); internal static class SmokeGuard { public static bool IsSmoke => string.Equals(RuntimeDiagnostics.RuntimeMode, "Smoke", StringComparison.OrdinalIgnoreCase); public static void EnsureReadOnly() { if (IsSmoke) throw new SmokeWriteBlockedException(); } }