Parent
Sub-issue of #28.
Summary
Implement the in-app log viewer per .claude/rules/onedrive-logviewer.md. Self-contained — no account dependency.
Scope (~4 production files)
InMemoryLogSink — Serilog sink + ILogEntryProvider; ring buffer (500 entries); PII scrubbing (email, OS username); thread-safe via ConcurrentQueue + Subject<LogEntry>
ILogEntryProvider — interface: IObservable<LogEntry> EntryAdded + IReadOnlyList<LogEntry> GetSnapshot()
LogViewerViewModel — subscribes to EntryAdded, loads snapshot on construction, marshals to UI thread via RxApp.MainThreadScheduler; ObservableCollection<LogEntry> Entries; level filter
LogViewerView.axaml + .axaml.cs — list bound to Entries, level filter dropdown, auto-scroll toggle
- DI registration in startup (
InMemoryLogSink as singleton for both ILogEventSink and ILogEntryProvider)
Acceptance criteria
Depends on
- #28a (settings shell to host the viewer)
Parent
Sub-issue of #28.
Summary
Implement the in-app log viewer per
.claude/rules/onedrive-logviewer.md. Self-contained — no account dependency.Scope (~4 production files)
InMemoryLogSink— Serilog sink +ILogEntryProvider; ring buffer (500 entries); PII scrubbing (email, OS username); thread-safe viaConcurrentQueue+Subject<LogEntry>ILogEntryProvider— interface:IObservable<LogEntry> EntryAdded+IReadOnlyList<LogEntry> GetSnapshot()LogViewerViewModel— subscribes toEntryAdded, loads snapshot on construction, marshals to UI thread viaRxApp.MainThreadScheduler;ObservableCollection<LogEntry> Entries; level filterLogViewerView.axaml+.axaml.cs— list bound toEntries, level filter dropdown, auto-scroll toggleInMemoryLogSinkas singleton for bothILogEventSinkandILogEntryProvider)Acceptance criteria
dotnet buildzero warnings;dotnet testall passDepends on