diff --git a/docs/agent/gather-rule-debug-log.md b/docs/agent/gather-rule-debug-log.md index 7df57b5d..7d4dfdd4 100644 --- a/docs/agent/gather-rule-debug-log.md +++ b/docs/agent/gather-rule-debug-log.md @@ -44,7 +44,7 @@ tenant-config report. `RemoteConfigMerger` projects the bool onto a runtime path (same pattern as `EnableImeMatchLog`): `true` → `Constants.GatherRuleDebugLogPath` = -`%ProgramData%\AutopilotMonitor\Logs\gather_rules_debug.log`; `false` → null, unless a +`%ProgramData%\AutopilotMonitor\Logs\gather-rules-debug.log`; `false` → null, unless a `--gather-debug-log ` CLI override supplied a non-default path. The field is not security-sensitive and deliberately **survives the offline config cache**. diff --git a/docs/agent/logs-and-persistence.md b/docs/agent/logs-and-persistence.md index 9d6bd12e..a44ac417 100644 --- a/docs/agent/logs-and-persistence.md +++ b/docs/agent/logs-and-persistence.md @@ -53,7 +53,7 @@ every line; the spool fsyncs only items flagged `RequiresImmediateFlush`. | Event sequence | `State\event-sequence.json` | `{ LastAssignedSequence }` | atomic | none; survives WhiteGlove Part-2 resume | | Spool | `Spool\spool.jsonl` | JSONL (`TelemetryItem`/line) | only `RequiresImmediateFlush` items | none — never truncated, cursor skips uploaded items | | Upload cursor | `Spool\upload-cursor.json` | `{ LastUploadedItemId }` | atomic | idempotent, never regresses | -| Agent trace log | `Logs\agent_YYYYMMDD[_NNN].log` | text `[utc] [LEVEL] msg` | no | 50 MB → suffix rotation; consumed by `/diag-agent-log` | +| Agent trace log | `Logs\agent.log` (segments `agent-NNN.log`) | text `[utc] [LEVEL] msg` | no | 50 MB → suffix rotation; consumed by `/diag-agent-log` | | Crash dump + record | `Crashes\.dmp` + `.json` | MiniDump + JSON | n/a (process dying) | 5 files / 7 days | | Startup-event gate | `State\startup-event-state.json` | JSON dict | no (atomic) | cross-reboot dedup of one-shot probes | | Bandwidth state | `State\bandwidth-state.json` | JSON samples/counters | no (atomic) | reboot-survivor for the DO bandwidth estimate | diff --git a/docs/agent/overview.md b/docs/agent/overview.md index 3a8ebeec..466b0511 100644 --- a/docs/agent/overview.md +++ b/docs/agent/overview.md @@ -146,7 +146,7 @@ snapshots) are executed by the `EffectRunner` — the reducer itself is pure. De | `\Agent\` | deployed binaries | | `\State\` | decision persistence (SignalLog, Journal, Snapshot), `enrollment-complete.marker`, `final-status.json`, self-update state, `.quarantine/`, `.part1-/` | | `\Spool\` | telemetry spool (offline queue) | -| `\Logs\` | agent trace log, `crash_*.log`, `ime_pattern_matches.log` | +| `\Logs\` | agent trace log (`agent.log`), `crash-*.log`, `ime-pattern-matches.log` | | `\Config\` | `remote-config.json` cache | | `\Agent-Update\`, `\Updates\` | self-update staging/download | diff --git a/docs/log.md b/docs/log.md index 8ede69da..7a7f0cce 100644 --- a/docs/log.md +++ b/docs/log.md @@ -2,6 +2,7 @@ ## 2026-08-18 +* **Update**: `agent/logs-and-persistence.md`, `agent/overview.md`, `agent/gather-rule-debug-log.md` — log files renamed to unified kebab-case: `agent.log` (date rotation dropped; size segments `agent-NNN.log`), `bootstrap-script.log` (was `bootstrap_agent.log`), `bootstrap-msi.log` (was `msi-bootstrap.log`), `crash-*.log`, `gather-rules-debug.log`, `ime-pattern-matches.log`. Old names still appear in archives from pre-rename agents. * **New**: `agent/registry-app-state-pillar.md` — the registry second pillar for IME app state (audit follow-up): snapshot-and-diff watcher on `HKLM\...\IntuneManagementExtension` (Win32Apps / EspTrackingWin32Apps / StatusServiceReports), `registry_app_state` + `app_state_reconciliation` events, silent startup baseline, WDP reuse notes. ## 2026-08-17 diff --git a/scripts/Bootstrap/Install-AutopilotMonitor.ps1 b/scripts/Bootstrap/Install-AutopilotMonitor.ps1 index 63ea5fdb..751fa4f4 100644 --- a/scripts/Bootstrap/Install-AutopilotMonitor.ps1 +++ b/scripts/Bootstrap/Install-AutopilotMonitor.ps1 @@ -95,6 +95,8 @@ Organizations). SKIP-safe when the API is absent (< 1809). Removed legacy Content-MD5 fallback; SHA-256 manifest verification is now mandatory (no hash -> bootstrap aborts, no unverified install). + 2026-08-18 v2.4 Renamed log file bootstrap_agent.log -> bootstrap-script.log + (unified kebab-case log naming across the agent log directory). 2026-05-09 v2.0 Generic bootstrap: agent owns its own defaults (e.g. 600 s TenantId-wait), so the script only calls `--install` plain. Hardened post-install with a 10 s runtime-process verify. @@ -126,13 +128,13 @@ param( ) # Script version (bump on meaningful changes; see .CHANGELOG above) -$ScriptVersion = "2.3" +$ScriptVersion = "2.4" # Configuration - Everything in ProgramData for easy cleanup $AgentBasePath = "$env:ProgramData\AutopilotMonitor" $AgentBinPath = "$AgentBasePath\Agent" $AgentLogPath = "$AgentBasePath\Logs" -$LogFile = "$AgentLogPath\bootstrap_agent.log" +$LogFile = "$AgentLogPath\bootstrap-script.log" # Write-Host (NOT Write-Output): log calls happen inside functions that return # decision objects; Write-Output would leak every log line into those return values. diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Logging/AgentLoggerTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Logging/AgentLoggerTests.cs index 387dbe13..f5dbb497 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Logging/AgentLoggerTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Logging/AgentLoggerTests.cs @@ -15,7 +15,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Logging /// public sealed class AgentLoggerTests { - private static string TodayBaseName => $"agent_{DateTime.Now:yyyyMMdd}"; + private const string BaseName = "agent"; [Fact] public void Log_writes_to_unsuffixed_base_file_when_dir_is_empty() @@ -25,7 +25,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Logging logger.Info("hello"); - var basePath = Path.Combine(tmp.Path, TodayBaseName + ".log"); + var basePath = Path.Combine(tmp.Path, BaseName + ".log"); Assert.True(File.Exists(basePath), $"expected base file at {basePath}"); Assert.Contains("hello", File.ReadAllText(basePath)); } @@ -39,17 +39,17 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Logging // Each line ~80 bytes after timestamp/severity envelope; 30 lines clears 1 KB // even at the smallest payload. The first lines fill the base file; once the - // FileInfo.Length read sees >= 1024 the next call rotates to _002. + // FileInfo.Length read sees >= 1024 the next call rotates to -002. for (int i = 0; i < 60; i++) { logger.Info($"line-{i:D4}-padded-with-some-extra-context-to-cross-1KB-quickly"); } - var files = Directory.GetFiles(tmp.Path, TodayBaseName + "*.log").OrderBy(s => s).ToArray(); + var files = Directory.GetFiles(tmp.Path, BaseName + "*.log").OrderBy(s => s).ToArray(); Assert.True(files.Length >= 2, $"expected at least 2 segments, got: {string.Join(",", files.Select(Path.GetFileName))}"); - // The unsuffixed file is the first segment; rotated segments use _002 onward. - var rotated = files.FirstOrDefault(p => Path.GetFileNameWithoutExtension(p).EndsWith("_002")); + // The unsuffixed file is the first segment; rotated segments use -002 onward. + var rotated = files.FirstOrDefault(p => Path.GetFileNameWithoutExtension(p).EndsWith("-002")); Assert.NotNull(rotated); } @@ -64,8 +64,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Logging logger.Info($"pad-{i:D4}-content-content-content-content-content-content"); } - var rotatedPath = Path.Combine(tmp.Path, TodayBaseName + "_002.log"); - Assert.True(File.Exists(rotatedPath), "expected agent_YYYYMMDD_002.log to exist"); + var rotatedPath = Path.Combine(tmp.Path, BaseName + "-002.log"); + Assert.True(File.Exists(rotatedPath), "expected agent-002.log to exist"); var first = File.ReadAllLines(rotatedPath).FirstOrDefault(); Assert.NotNull(first); Assert.Contains("AgentLogger: rotated", first); @@ -75,23 +75,23 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Logging public void Log_continues_existing_rotation_on_restart_without_overwriting() { using var tmp = new TempDirectory(); - // Pre-seed: looks like an earlier process already rotated up to _003. - File.WriteAllText(Path.Combine(tmp.Path, TodayBaseName + ".log"), "older-base\n"); - File.WriteAllText(Path.Combine(tmp.Path, TodayBaseName + "_002.log"), "older-002\n"); - File.WriteAllText(Path.Combine(tmp.Path, TodayBaseName + "_003.log"), "older-003\n"); + // Pre-seed: looks like an earlier process already rotated up to -003. + File.WriteAllText(Path.Combine(tmp.Path, BaseName + ".log"), "older-base\n"); + File.WriteAllText(Path.Combine(tmp.Path, BaseName + "-002.log"), "older-002\n"); + File.WriteAllText(Path.Combine(tmp.Path, BaseName + "-003.log"), "older-003\n"); var logger = new AgentLogger(tmp.Path, AgentLogLevel.Info, maxFileSizeBytes: 1024L * 1024); logger.Info("after-restart"); - // New entries must land in the highest existing segment (_003), not in a fresh + // New entries must land in the highest existing segment (-003), not in a fresh // base file. Older content in _003 must be preserved. - var resumed = File.ReadAllText(Path.Combine(tmp.Path, TodayBaseName + "_003.log")); + var resumed = File.ReadAllText(Path.Combine(tmp.Path, BaseName + "-003.log")); Assert.Contains("older-003", resumed); Assert.Contains("after-restart", resumed); // Older segments are left alone. - Assert.Equal("older-base\n", File.ReadAllText(Path.Combine(tmp.Path, TodayBaseName + ".log"))); - Assert.Equal("older-002\n", File.ReadAllText(Path.Combine(tmp.Path, TodayBaseName + "_002.log"))); + Assert.Equal("older-base\n", File.ReadAllText(Path.Combine(tmp.Path, BaseName + ".log"))); + Assert.Equal("older-002\n", File.ReadAllText(Path.Combine(tmp.Path, BaseName + "-002.log"))); } [Fact] @@ -105,7 +105,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Logging // as plain ASCII, while genuine non-ASCII payload stays untouched. logger.Info("check failed \u2014 range 1\u20132 \u2192 done\u2026 \u2018a\u2019 \u201Cb\u201D nb\u00A0sp f\u00FCr M\u00FCller"); - var text = File.ReadAllText(Path.Combine(tmp.Path, TodayBaseName + ".log")); + var text = File.ReadAllText(Path.Combine(tmp.Path, BaseName + ".log")); Assert.Contains("check failed - range 1-2 -> done... 'a' \"b\" nb sp f\u00FCr M\u00FCller", text); } @@ -115,8 +115,8 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Logging using var tmp = new TempDirectory(); var logger = new AgentLogger(tmp.Path, AgentLogLevel.Info, maxFileSizeBytes: 1024L * 1024); - // Hold an exclusive lock on today's file to force AppendAllText to throw. - var lockedPath = Path.Combine(tmp.Path, TodayBaseName + ".log"); + // Hold an exclusive lock on the log file to force AppendAllText to throw. + var lockedPath = Path.Combine(tmp.Path, BaseName + ".log"); File.WriteAllText(lockedPath, ""); // ensure exists using var holder = new FileStream(lockedPath, FileMode.Open, FileAccess.ReadWrite, FileShare.None); diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleDebugLogTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleDebugLogTests.cs index cdecd3ff..271e1575 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleDebugLogTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleDebugLogTests.cs @@ -25,7 +25,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Gather public void Dispose() => _tmp.Dispose(); - private string LogPath => Path.Combine(_tmp.Path, "sub", "gather_rules_debug.log"); + private string LogPath => Path.Combine(_tmp.Path, "sub", "gather-rules-debug.log"); [Fact] public void Write_produces_pipe_delimited_line_and_creates_directory_lazily() @@ -73,7 +73,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Gather for (int i = 0; i < 20; i++) log.Write("RULE-1", GatherRuleDebugLog.StageExec, new string('x', 100)); - var oldPath = Path.Combine(Path.GetDirectoryName(LogPath), "gather_rules_debug.old.log"); + var oldPath = Path.Combine(Path.GetDirectoryName(LogPath), "gather-rules-debug.old.log"); Assert.True(File.Exists(oldPath), "expected one .old rotation generation"); Assert.True(File.Exists(LogPath)); Assert.Contains("-- rotated", File.ReadAllText(LogPath) + File.ReadAllText(oldPath)); diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleExecutorDebugLogTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleExecutorDebugLogTests.cs index d7251892..7b6d504f 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleExecutorDebugLogTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/GatherRuleExecutorDebugLogTests.cs @@ -28,7 +28,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Gather public GatherRuleExecutorDebugLogTests() { var logger = new AgentLogger(_tmp.Path, AgentLogLevel.Info); - _debugLogPath = Path.Combine(_tmp.Path, "gather_rules_debug.log"); + _debugLogPath = Path.Combine(_tmp.Path, "gather-rules-debug.log"); _executor = new GatherRuleExecutor( "sess", "tenant", _ => { }, logger, debugLogPath: _debugLogPath) diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/LogParserCollectorDebugLogTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/LogParserCollectorDebugLogTests.cs index e1a1a698..14b43c2e 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/LogParserCollectorDebugLogTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Monitoring/Gather/LogParserCollectorDebugLogTests.cs @@ -32,7 +32,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Monitoring.Gather { Directory.CreateDirectory(_outsideUsers); var logger = new AgentLogger(_tmp.Path, AgentLogLevel.Info); - _debugLogPath = Path.Combine(_tmp.Path, "gather_rules_debug.log"); + _debugLogPath = Path.Combine(_tmp.Path, "gather-rules-debug.log"); _context = new GatherRuleContext( logger, "sess", "tenant", evt => _events.Add(evt), diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Program/ProgramGuardsTests.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Program/ProgramGuardsTests.cs index 840d4ec4..9698af42 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Program/ProgramGuardsTests.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core.Tests/Program/ProgramGuardsTests.cs @@ -53,7 +53,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Program using var tmp = new TempDirectory(); var logDir = Path.Combine(tmp.Path, "logs"); Directory.CreateDirectory(logDir); - var crashPath = Path.Combine(logDir, "crash_20260421_100000.log"); + var crashPath = Path.Combine(logDir, "crash-20260421-100000.log"); File.WriteAllText(crashPath, "[2026-04-21T10:00:00Z] FATAL: InvalidOperationException: simulated crash"); var summary = AutopilotMonitor.Agent.V2.Program.DetectPreviousExit(tmp.Path, logDir); @@ -312,7 +312,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Tests.Program AutopilotMonitor.Agent.V2.Program.WriteCrashLog(logDir, new InvalidOperationException("boom")); - var files = Directory.GetFiles(logDir, "crash_*.log"); + var files = Directory.GetFiles(logDir, "crash-*.log"); Assert.Single(files); var content = File.ReadAllText(files[0]); Assert.Contains("FATAL:", content); diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/Logging/AgentLogger.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/Logging/AgentLogger.cs index ed3d4915..05c47245 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core/Logging/AgentLogger.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/Logging/AgentLogger.cs @@ -34,9 +34,9 @@ namespace AutopilotMonitor.Agent.V2.Core.Logging private readonly long _maxFileSizeBytes; private readonly string _logDirectory; - private readonly string _logFileBaseName; // "agent_20260425" - private string _logFilePath; // mutable — rotates to "_002", "_003" ... - private int _rotationSuffix; // 0 = base file, 2+ = "_002" etc. + private readonly string _logFileBaseName; // "agent" + private string _logFilePath; // mutable — rotates to "-002", "-003" ... + private int _rotationSuffix; // 0 = base file, 2+ = "-002" etc. /// /// When true, log entries are also written to Console.Out (same format as the log file). @@ -55,14 +55,19 @@ namespace AutopilotMonitor.Agent.V2.Core.Logging Directory.CreateDirectory(logDirectory); } - _logFileBaseName = $"agent_{DateTime.Now:yyyyMMdd}"; + // One canonical name, no date: the agent only lives for the duration of an + // enrollment, and a date-based split rotates at local midnight — which can fall + // mid-enrollment (local time may still be on the OOBE default timezone). Size + // rotation below bounds pathological growth. Legacy `agent_YYYYMMDD*.log` files + // from a pre-rename agent are ignored by the suffix probe and left in place. + _logFileBaseName = "agent"; _rotationSuffix = ProbeNextRotationSuffix(logDirectory, _logFileBaseName); _logFilePath = BuildLogFilePath(); } - // Picks up the highest existing rotation suffix on the same day so a process restart - // doesn't overwrite previous segments. Returns 0 when no log file exists yet — the - // first segment uses the unsuffixed name `agent_YYYYMMDD.log`. + // Picks up the highest existing rotation suffix so a process restart doesn't + // overwrite previous segments. Returns 0 when no log file exists yet — the + // first segment uses the unsuffixed name `agent.log`. private static int ProbeNextRotationSuffix(string dir, string baseName) { if (!Directory.Exists(dir)) return 0; @@ -80,7 +85,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Logging continue; } if (name.Length > baseName.Length + 1 && - name.StartsWith(baseName + "_", StringComparison.OrdinalIgnoreCase) && + name.StartsWith(baseName + "-", StringComparison.OrdinalIgnoreCase) && int.TryParse(name.Substring(baseName.Length + 1), out var suffix) && suffix > max) { @@ -97,7 +102,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Logging { var name = _rotationSuffix < 2 ? $"{_logFileBaseName}.log" - : $"{_logFileBaseName}_{_rotationSuffix:D3}.log"; + : $"{_logFileBaseName}-{_rotationSuffix:D3}.log"; return Path.Combine(_logDirectory, name); } diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Enrollment/SystemSignals/StallProbeCollector.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Enrollment/SystemSignals/StallProbeCollector.cs index 5fa575ba..da446563 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Enrollment/SystemSignals/StallProbeCollector.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Enrollment/SystemSignals/StallProbeCollector.cs @@ -244,7 +244,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Enrollment.SystemSignals result.DurationMs = (int)(DateTime.UtcNow - started).TotalMilliseconds; - // Always log locally for diagnostics — the agent_YYYYMMDD.log is the always-on observability. + // Always log locally for diagnostics — agent.log is the always-on observability. var anomaliesStr = result.Anomalies.Count > 0 ? $"anomalies=[{string.Join(",", result.Anomalies)}]" : "quiet"; diff --git a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Runtime/SelfUpdater.cs b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Runtime/SelfUpdater.cs index 20bc986d..ad828e26 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Runtime/SelfUpdater.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2.Core/Monitoring/Runtime/SelfUpdater.cs @@ -972,7 +972,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Runtime /// /// Appends a log line to the main agent log file (logger isn't initialized yet at update time). - /// Uses the same date-based naming and format as AgentLogger: agent_YYYYMMDD.log + /// Uses the same naming and format as AgentLogger: agent.log /// private static void LogToFile(string message) { @@ -980,8 +980,7 @@ namespace AutopilotMonitor.Agent.V2.Core.Monitoring.Runtime { var logDir = Environment.ExpandEnvironmentVariables(Constants.LogDirectory); Directory.CreateDirectory(logDir); - var logFileName = $"agent_{DateTime.Now:yyyyMMdd}.log"; - var logPath = Path.Combine(logDir, logFileName); + var logPath = Path.Combine(logDir, "agent.log"); File.AppendAllText(logPath, $"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss.fff}] [INFO] {message}{Environment.NewLine}"); } catch diff --git a/src/Agent/AutopilotMonitor.Agent.V2/Program.Guards.cs b/src/Agent/AutopilotMonitor.Agent.V2/Program.Guards.cs index 4f0ac68b..28cc5154 100644 --- a/src/Agent/AutopilotMonitor.Agent.V2/Program.Guards.cs +++ b/src/Agent/AutopilotMonitor.Agent.V2/Program.Guards.cs @@ -26,7 +26,7 @@ namespace AutopilotMonitor.Agent.V2 /// cleanup and exit. /// Session-age emergency break — absolute cap (AbsoluteMaxSessionHours) /// across restarts prevents zombie agents from persisting forever. - /// Crash log writer — writes a last-resort crash_*.log next to the agent log + /// Crash log writer — writes a last-resort crash-*.log next to the agent log /// directory when an uncaught exception escapes Main. /// /// @@ -34,7 +34,10 @@ namespace AutopilotMonitor.Agent.V2 { internal const string CleanExitMarkerFileName = "clean-exit.marker"; internal const string EnrollmentCompleteMarkerFileName = "enrollment-complete.marker"; - private const string CrashLogPrefix = "crash_"; + private const string CrashLogPrefix = "crash-"; + // Pre-rename agents (< 2026-08-18) wrote crash_*.log; still detected once so an + // in-place self-update does not lose a pending crash classification. + private const string LegacyCrashLogPrefix = "crash_"; // ----------------------------------------------------------------- Multi-instance guard @@ -102,7 +105,7 @@ namespace AutopilotMonitor.Agent.V2 } /// - /// Reads clean-exit.marker + crash_*.log + session files + (if needed) the + /// Reads clean-exit.marker + crash-*.log + session files + (if needed) the /// Windows event log to classify the previous exit. Deletes the marker and crash logs so /// the next cycle starts fresh. Legacy parity — same rules, same outputs. /// @@ -114,6 +117,8 @@ namespace AutopilotMonitor.Agent.V2 var hadCleanExit = File.Exists(cleanExitMarker); var crashLogs = Directory.Exists(logDirectory) ? Directory.GetFiles(logDirectory, CrashLogPrefix + "*.log") + .Concat(Directory.GetFiles(logDirectory, LegacyCrashLogPrefix + "*.log")) + .ToArray() : Array.Empty(); if (hadCleanExit) @@ -406,7 +411,7 @@ namespace AutopilotMonitor.Agent.V2 try { Directory.CreateDirectory(logDirectory); - var crashPath = Path.Combine(logDirectory, $"{CrashLogPrefix}{DateTime.UtcNow:yyyyMMdd_HHmmss}.log"); + var crashPath = Path.Combine(logDirectory, $"{CrashLogPrefix}{DateTime.UtcNow:yyyyMMdd-HHmmss}.log"); File.WriteAllText(crashPath, $"[{DateTime.UtcNow:u}] FATAL: {ex}"); } catch { /* nowhere left to log */ } diff --git a/src/Agent/AutopilotMonitor.BootstrapMsi/Invoke-BootstrapDownload.ps1 b/src/Agent/AutopilotMonitor.BootstrapMsi/Invoke-BootstrapDownload.ps1 index 14ac61c9..3964c88b 100644 --- a/src/Agent/AutopilotMonitor.BootstrapMsi/Invoke-BootstrapDownload.ps1 +++ b/src/Agent/AutopilotMonitor.BootstrapMsi/Invoke-BootstrapDownload.ps1 @@ -7,14 +7,14 @@ # server-updatable without re-shipping the MSI. # # Fail-soft by design: the runner always exits 0 so a transient network failure never -# marks the LOB install as failed; diagnostics go to msi-bootstrap.log. +# marks the LOB install as failed; diagnostics go to bootstrap-msi.log. # ASCII only - no special characters in this file. $ErrorActionPreference = 'Stop' $baseDir = Join-Path $env:ProgramData 'AutopilotMonitor' $logDir = Join-Path $baseDir 'Logs' New-Item -ItemType Directory -Force -Path $logDir | Out-Null -$log = Join-Path $logDir 'msi-bootstrap.log' +$log = Join-Path $logDir 'bootstrap-msi.log' function Write-Log([string]$message) { Add-Content -Path $log -Value ("[{0}] {1}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $message) diff --git a/src/Agent/AutopilotMonitor.BootstrapMsi/Package.wxs b/src/Agent/AutopilotMonitor.BootstrapMsi/Package.wxs index 028f67f6..81a5c639 100644 --- a/src/Agent/AutopilotMonitor.BootstrapMsi/Package.wxs +++ b/src/Agent/AutopilotMonitor.BootstrapMsi/Package.wxs @@ -39,7 +39,7 @@ + additionally fail-soft itself); diagnostics live in bootstrap-msi.log. --> /// Default path for the IME pattern match log file (debugging/diagnostics) /// - public const string ImeMatchLogPath = @"%ProgramData%\AutopilotMonitor\Logs\ime_pattern_matches.log"; + public const string ImeMatchLogPath = @"%ProgramData%\AutopilotMonitor\Logs\ime-pattern-matches.log"; /// /// Default path for the gather rule debug trace file (debugging/diagnostics). /// Written when the tenant enables EnableGatherRuleDebugLog; explains every /// gather-rule evaluation decision, including outcomes that produce no event. /// - public const string GatherRuleDebugLogPath = @"%ProgramData%\AutopilotMonitor\Logs\gather_rules_debug.log"; + public const string GatherRuleDebugLogPath = @"%ProgramData%\AutopilotMonitor\Logs\gather-rules-debug.log"; /// /// Scheduled Task name used to run the agent as SYSTEM diff --git a/src/Web/autopilot-monitor-web/app/settings/components/AgentSettingsSection.tsx b/src/Web/autopilot-monitor-web/app/settings/components/AgentSettingsSection.tsx index f38ac48a..0d495431 100644 --- a/src/Web/autopilot-monitor-web/app/settings/components/AgentSettingsSection.tsx +++ b/src/Web/autopilot-monitor-web/app/settings/components/AgentSettingsSection.tsx @@ -194,7 +194,7 @@ export default function AgentSettingsSection({

IME Pattern Match Log

Write every matched IME log line to a local file for diagnostics - {enableImeMatchLog && %ProgramData%\AutopilotMonitor\Logs\ime_pattern_matches.log} + {enableImeMatchLog && %ProgramData%\AutopilotMonitor\Logs\ime-pattern-matches.log}