feat(ui): add current-launch logs tab
Add Live Chat and Logs tabs to the non-edit left panel. Expose a bounded process-local AppLog feed so the newest messages appear first without importing earlier startup.log contents or changing file logging. Ref: https://obsproject.com/forum/resources/understanding-obs-studio-log.473
This commit is contained in:
+19
-1
@@ -12,6 +12,7 @@ public static class AppLog
|
||||
{
|
||||
/// <summary>The cap for the live log file. Past this, it rolls to <c>startup.1.log</c>.</summary>
|
||||
private const long DefaultMaxBytes = 5 * 1024 * 1024;
|
||||
private const int CurrentLaunchEntryLimit = 500;
|
||||
|
||||
/// <summary>How many rolled copies to keep (startup.1.log, startup.2.log, …).</summary>
|
||||
private const int RetainedFiles = 1;
|
||||
@@ -20,6 +21,8 @@ public static class AppLog
|
||||
InstanceProfile.DataRoot, "startup.log");
|
||||
|
||||
private static readonly object Gate = new();
|
||||
private static readonly List<AppLogEntry> CurrentLaunchEntries = new();
|
||||
private static long _nextSequence;
|
||||
|
||||
/// <summary>Test seam: point the log somewhere else. Null = the real profile's log.</summary>
|
||||
internal static string? PathOverride { get; set; }
|
||||
@@ -29,16 +32,29 @@ public static class AppLog
|
||||
|
||||
private static string LogPath => PathOverride ?? DefaultLogPath;
|
||||
|
||||
internal static IReadOnlyList<AppLogEntry> SnapshotCurrentLaunch()
|
||||
{
|
||||
lock (Gate)
|
||||
return CurrentLaunchEntries.ToArray();
|
||||
}
|
||||
|
||||
public static void Write(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (Gate)
|
||||
{
|
||||
var entry = new AppLogEntry(
|
||||
++_nextSequence,
|
||||
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {message}");
|
||||
CurrentLaunchEntries.Add(entry);
|
||||
if (CurrentLaunchEntries.Count > CurrentLaunchEntryLimit)
|
||||
CurrentLaunchEntries.RemoveAt(0);
|
||||
|
||||
var path = LogPath;
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
|
||||
LogRotation.RollIfNeeded(path, MaxBytes, RetainedFiles);
|
||||
File.AppendAllText(path, $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} {message}{Environment.NewLine}");
|
||||
File.AppendAllText(path, $"{entry.Text}{Environment.NewLine}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -52,3 +68,5 @@ public static class AppLog
|
||||
Write($"{message}: {exception}");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed record AppLogEntry(long Sequence, string Text);
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ Cross-cutting utilities. See [`schema.md`](../schema.md) for the memory-map conv
|
||||
|------|---------|
|
||||
| `ViewModelBase.cs` | `INotifyPropertyChanged` + `SetProperty<T>()` — base for all ViewModels |
|
||||
| `RelayCommand.cs` | `ICommand` implementation for all button actions |
|
||||
| `AppLog.cs` | File logger to `%APPDATA%\ytLlive\startup.log`; startup checkpoints + unhandled-exception capture (the crash this map is named for). Size-capped: rolls to `startup.1.log` at 5 MB (TASK 55) |
|
||||
| `AppLog.cs` | File logger to `%APPDATA%\ytLlive\startup.log`; startup checkpoints + unhandled-exception capture. Size-capped: rolls to `startup.1.log` at 5 MB (TASK 55). Also keeps the newest 500 formatted entries in a process-local buffer for the Logs tab; it never loads prior-launch file contents |
|
||||
| `LogRotation.cs` | The size-capped-rotation policy as a pure function over a path: roll `startup.log` → `startup.1.log` when it passes the cap, keeping N rolled copies. Separate from `AppLog` so the logger's 156-call-site surface never changes and the rule is testable alone (TASK 55) |
|
||||
| `FocusPreservingListBox.cs` | `ListBox` subclass that keeps selection/focus coherent when the selected item is deleted; `IsSelectable` predicate skips hidden scenes; drag-reorder guard |
|
||||
| `OAuthCredentials.cs` | Baked-in Google OAuth client ID/secret (desktop app; loopback callback) |
|
||||
|
||||
Reference in New Issue
Block a user