Add filter, virtualization, and export UI to DebugLogModal and unify file save#513
Add filter, virtualization, and export UI to DebugLogModal and unify file save#513
Conversation
There was a problem hiding this comment.
Pull request overview
This PR redesigns the debug log experience in the components/UI layers by moving from raw line rendering to parsed entries plus filtered/virtualized projection, and it centralizes the app’s Windows file-save flow behind a reusable IFileSaveService. It fits into the ongoing component/library restructuring by keeping platform-specific save behavior in the MAUI head while letting lifted/shared UI code depend only on interfaces.
Changes:
- Adds structured debug-log parsing/projection primitives and rewires
DebugLogModalto support level/text filtering, virtualization, export, copy, and improved clear/refresh behavior. - Introduces
IFileSaveServiceplus a MAUI/WinUI implementation, then refactors existing filter export paths to use it. - Adds parser/projection/component tests and shared test constants/utilities for the new debug-log behavior.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
src/EventLogExpert/wwwroot/css/app.css |
Adjusts shared modal footer layout to support extra footer content. |
src/EventLogExpert/Services/MauiFileSaveService.cs |
Adds Windows MAUI implementation for unified text file saving. |
src/EventLogExpert/MauiProgram.cs |
Registers the new file-save service in DI. |
src/EventLogExpert/Components/Modals/Filters/FilterGroupModal.razor.cs |
Refactors saved-group export to use IFileSaveService. |
src/EventLogExpert/Components/Modals/Filters/FilterGroup.razor.cs |
Refactors single-group export to use IFileSaveService. |
src/EventLogExpert/Components/Modals/Filters/FilterCacheModal.razor.cs |
Refactors saved-filter export to use IFileSaveService. |
src/EventLogExpert.UI/Services/DebugLogProjection.cs |
Adds pure projection/filtering logic for parsed debug-log entries. |
src/EventLogExpert.UI/Services/DebugLogEntryParser.cs |
Adds parsing/folding of raw debug-log lines into structured entries. |
src/EventLogExpert.UI/Models/DebugLogEntry.cs |
Defines the structured debug-log entry record. |
src/EventLogExpert.UI/Interfaces/IFileSaveService.cs |
Introduces the shared file-save abstraction and common file-type maps. |
src/EventLogExpert.UI.Tests/TestUtils/Constants/Constants.DebugLog.cs |
Adds shared UI-test constants for debug-log parsing/projection tests. |
src/EventLogExpert.UI.Tests/Services/DebugLogProjectionTests.cs |
Covers projection ordering and filter behavior. |
src/EventLogExpert.UI.Tests/Services/DebugLogEntryParserTests.cs |
Covers parser prefix handling, malformed lines, and continuation folding. |
src/EventLogExpert.Components/_Imports.razor |
Imports Blazor virtualization support for components. |
src/EventLogExpert.Components/Modals/DebugLogModal.razor.css |
Styles the new filter bar, virtualized rows, and footer layout. |
src/EventLogExpert.Components/Modals/DebugLogModal.razor.cs |
Reworks modal state/behavior for parsing, filtering, export/copy, and refresh/clear flows. |
src/EventLogExpert.Components/Modals/DebugLogModal.razor |
Replaces raw line rendering with filter controls, Virtualize, and enhanced footer actions. |
src/EventLogExpert.Components.Tests/TestUtils/DebugLogUtils.cs |
Adds component-test helpers for building async debug-log data. |
src/EventLogExpert.Components.Tests/TestUtils/Constants/Constants.DebugLog.cs |
Adds shared component-test constants for debug-log modal tests. |
src/EventLogExpert.Components.Tests/Modals/DebugLogModalTests.cs |
Adds bUnit coverage for modal filtering, counter, export, copy, and clear behavior. |
Comments suppressed due to low confidence (1)
src/EventLogExpert.Components/Modals/DebugLogModal.razor.cs:188
LoadAsync()can still throw here (for example on access or read failures), and with only atry/finallythe exception will bubble out of the event/lifecycle handler. That means opening or refreshing the modal can still fail as an unhandled UI exception instead of surfacing a recoverable alert likeClear/Exportdo.
try
{
await foreach (var line in FileLogger.LoadAsync())
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
465a017 to
8bc40d3
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
8bc40d3 to
bd398d6
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
bd398d6 to
14d41c4
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
14d41c4 to
fd4f150
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fd4f150 to
6a259c9
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
6a259c9 to
4cfd79d
Compare
c128aff to
ff5ae03
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
ff5ae03 to
9de514e
Compare
9de514e to
b4cd4d3
Compare
b4cd4d3 to
a23bc88
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
a23bc88 to
fdcb1d5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
fdcb1d5 to
214b2af
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
214b2af to
1b4378e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 31 out of 31 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
1b4378e to
dbb78c4
Compare
dbb78c4 to
b163985
Compare
Summary
Redesigns the debug log modal with filters, virtualized rendering, and export/copy actions, and unifies the existing file-save export pattern (used by 3 other modals) behind a new
IFileSaveService.What this changes
DebugLogModal redesign
DebugLogEntryrecord +DebugLogEntryParserparses[ISO-Timestamp] [ThreadId] [Level] messagelines, folding continuation lines (stack traces) into the prior entry.DebugLogProjection(pure) applies the active filters and reverses into newest-entry-first order viaReversedListView<T>(now implementsIList<T>so Blazor<Virtualize>gets theEnumerable.Skip/Takeindexer fast-path for deep offsets).<Virtualize>(RowHeightPx=18initial estimate, monospace font,white-space: pre-wrap+overflow-wrap: anywhereso long lines and stack traces wrap within the viewport instead of forcing a horizontal scrollbar). Multi-line entries render across multiple visual lines, which makes<Virtualize>''s scroll-position math approximate for tall rows — accepted tradeoff vs. the prior nowrap+ellipsis approach (which was inaccessible to keyboard / touch users) and the prior horizontal-scroll approach (which the user explicitly disliked).ValueSelectwithEquals/NotEqual/MultiSelectoperators) and a debounced (250ms) message string filter.X of Y entriescounter.Refreshwraps the streamingLoadAsyncintry/finallyso partial data and_hasLoadedremain consistent on exception (generation-guarded).ClearawaitsFileLogger.ClearAsync()first, mutates UI state only on success, surfaces failures viaIAlertDialogService.DebugLogService.WriteTraceformats the timestamp inside_writeLock.EnterScope()so the on-disk line order is monotonic in timestamp under contention. Backed by a newTrace_WhenCalledFromMultipleThreadsConcurrently_*regression test that fires 64×4 parallel writes and asserts (a) line count, (b) every line parses, (c) timestamps non-decreasing, (d) every unique payload appears exactly once.Unified file-save service
IFileSaveService(inEventLogExpert.UI.Interfaces) +FileSaveServiceFileTypes(Json/Logtyped dicts) as the single source of truth for file-type choices.MauiFileSaveServiceimplementation registered inMauiProgram. AddsSetLength(0)beforeWriteAsyncso overwriting a larger existing file no longer leaves stale trailing bytes (rationale documented inline).FilterGroup.ExportGroup,FilterCacheModal.OnExportAsync,FilterGroupModal.OnExportAsync) onto the new service, removing directWindows.Storage.Pickers/WinRT.Interopdependencies from thoserazor.csfiles.Tests
DebugLogEntryParserTests(prefix parsing, continuation folding, malformed lines, no-prior-entry edge cases).DebugLogProjectionTests(level operators, text filter case-insensitivity, ordering, empty filters).DebugLogModalTests(bUnit) covering filter bar interactions, counter text, export call, clear success/failure, copy.ReversedListViewTestscoveringIndexOf(reversed semantics),Insert/RemoveAt/ indexer setter throwingNotSupportedException, and theIList<T>interface assertion.DebugLogServiceTestsconcurrent-write regression test (64×4 parallel writers, four invariants).Notes for reviewers
MauiFileSaveServicetruncation order (SetLength(0)beforeWriteAsync) was deliberately chosen over a temp-file-and-rename pattern. Trade-off: a rare mid-write failure leaves a partial file on disk and surfaces the propagated exception (loudly visible) rather than the prior bug''s silent corruption (mix of new prefix + stale tail). Note this is not a temp-file-and-rename atomic write; partial-file-with-loud-failure is preferred over silent truncation-by-overwrite. Comment in source documents this.Clear()reorder is a small behavior change to a pre-existing path — file is now mutated before the UI, with explicit failure surfacing. Two new tests cover both branches.DebugLogModal(ex.Message) and the 3 filter modals ($"An exception occurred while exporting X: {ex.Message}") is intentional — each surface describes its own context.DebugLogEntryParseris documented and intentional: the parser classifies any line matching the[ts] [tid] [Level]prefix as a new entry. A continuation line whose payload happens to start with that exact shape would be split as a new entry. Round 13 deliberately removed the previous timestamp-based heuristic because pre-PR-Add filter, virtualization, and export UI to DebugLogModal and unify file save #513 writers formatted the timestamp before acquiring the lock, so persisted legacydebug.logfiles can legitimately contain headers in non-chronological order — a timestamp-based heuristic that folded an "older-looking" header into the prior entry would corrupt those legacy logs. The format is inherently ambiguous when message bodies contain header-shaped lines; we accept that ambiguity (no current producer in this repo intentionally emits header-shaped continuation content) rather than corrupt legacy logs.