feat(ui): color and acknowledge pinned log errors

Add independent AppLog message types and explicit Class 1/2 severity. Wrap and color log rows, pin Class 1 above Class 2, and let acknowledgement return entries to the current-launch timeline without changing file format or rotation.

Ref: https://logtape.org/manual/levels
This commit is contained in:
2026-11-02 18:17:18 -08:00
parent 01a3fcaaf7
commit a6aea5bf26
23 changed files with 365 additions and 100 deletions
+13 -10
View File
@@ -103,7 +103,7 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: failed to start subprocess");
AppLog.Write(ex, "FFmpeg encoder: failed to start subprocess", AppLogErrorClass.Class2);
lock (_gate) _options = null;
throw;
}
@@ -175,7 +175,7 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: drain loop faulted during stop");
AppLog.Write(ex, "FFmpeg encoder: drain loop faulted during stop", AppLogErrorClass.Class2);
}
try
@@ -186,12 +186,13 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
}
catch (OperationCanceledException)
{
AppLog.Write("FFmpeg encoder: did not exit after stdin EOF — killing");
AppLog.Write("FFmpeg encoder: did not exit after stdin EOF — killing",
AppLogMessageType.Error, AppLogErrorClass.Class2);
process!.Kill();
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: waiting for exit failed");
AppLog.Write(ex, "FFmpeg encoder: waiting for exit failed", AppLogErrorClass.Class2);
process!.Kill();
}
@@ -201,7 +202,7 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: stderr loop faulted during stop");
AppLog.Write(ex, "FFmpeg encoder: stderr loop faulted during stop", AppLogErrorClass.Class2);
}
process.Dispose();
@@ -263,7 +264,7 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: drain loop write faulted");
AppLog.Write(ex, "FFmpeg encoder: drain loop write faulted", AppLogErrorClass.Class2);
}
finally
{
@@ -274,7 +275,7 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: drain loop faulted");
AppLog.Write(ex, "FFmpeg encoder: drain loop faulted", AppLogErrorClass.Class2);
}
});
}
@@ -303,7 +304,8 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: encoder probe failed — falling back to software");
AppLog.Write($"FFmpeg encoder: encoder probe failed — falling back to software: {ex}",
AppLogMessageType.Warning);
return FfmpegEncoderPicker.Preference[^1];
}
}
@@ -347,14 +349,15 @@ public sealed class FfmpegEncoder : IFfmpegEncoder
// The old `code != 0` gate let ffmpeg's clean early exit vanish
// without a word while the pump kept feeding a corpse
// (2026-09-01 first real recording: died at ~0.9s, zero logs).
AppLog.Write($"FFmpeg encoder: subprocess exited unexpectedly (code {exitCode})");
AppLog.Write($"FFmpeg encoder: subprocess exited unexpectedly (code {exitCode})",
AppLogMessageType.Error, AppLogErrorClass.Class2);
_health.LastError = $"FFmpeg exited with code {exitCode}";
ProcessFailed?.Invoke(this, $"FFmpeg exited with code {exitCode}");
}
}
catch (Exception ex)
{
AppLog.Write(ex, "FFmpeg encoder: stderr loop faulted");
AppLog.Write(ex, "FFmpeg encoder: stderr loop faulted", AppLogErrorClass.Class2);
}
});
}