Files
autodesk/Plant3D2026/P_Rec_Annot/src/PRecAnnot.Plant3D2026/RuntimeDiagnostics.cs
T

80 lines
3.2 KiB
C#

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 void Write(
string eventType,
string level = "INFO",
string? operationId = null,
string? commandName = null,
string? resultStatus = null,
System.Exception? exception = null,
Document? document = null,
IReadOnlyDictionary<string, object?>? details = null)
{
try
{
var payload = new Dictionary<string, object?>(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(); }
}