feat: added CSV export option to console logs download menu#18639
feat: added CSV export option to console logs download menu#18639IsxImattI wants to merge 9 commits into
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 18639Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 18639" |
There was a problem hiding this comment.
Pull request overview
This PR implements issue #11121 by adding a "Download as CSV" option to the dashboard console logs page, alongside the existing text download, both grouped under a "Download" submenu. The core addition is a new WriteLogEntriesToCsvStream serializer (Resource, Timestamp, Message columns with RFC 4180 escaping) that reuses the existing shared LogEntrySerializer and browser download infrastructure, so it fits cleanly into the established dashboard menu/download patterns.
Changes:
- Added
WriteLogEntriesToCsvStream(with RFC 4180 field escaping) to the sharedLogEntrySerializer, stripping the inline timestamp into its own column. - Restructured the single download menu item into a parent "Download" item with nested "Download as text" / "Download as CSV" entries, and split the download handler in two with an extension-aware
GetFileName. - Added localized strings (
.resx,.Designer.cs, all.xlfcultures) and 7 unit tests covering header, columns, escaping, and pause-skipping.
Reviewed changes
Copilot reviewed 17 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Shared/ConsoleLogs/LogEntrySerializer.cs | Adds the CSV serializer and RFC 4180 field-escaping helper. |
| src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs | Converts download to a submenu and adds the CSV download handler / extension-parameterized filename. |
| src/Aspire.Dashboard/Resources/ConsoleLogs.resx | Adds neutral DownloadLogsAsText / DownloadLogsAsCsv strings. |
| src/Aspire.Dashboard/Resources/ConsoleLogs.Designer.cs | Adds designer accessors for the two new strings. |
| src/Aspire.Dashboard/Resources/xlf/ConsoleLogs.*.xlf (13 files) | Auto-synced XLF entries (state="new") for the new strings. |
| tests/Aspire.Dashboard.Tests/ConsoleLogsTests/LogEntrySerializerTests.cs | New unit tests for CSV header, columns, escaping, and pause skipping. |
Files not reviewed (1)
- src/Aspire.Dashboard/Resources/ConsoleLogs.Designer.cs: Generated file
|
@microsoft-github-policy-service agree |
| [ | ||
| new() | ||
| { | ||
| OnClick = DownloadTextLogsAsync, |
There was a problem hiding this comment.
I’m going to carry the parent disabled condition onto these child download items too. Otherwise the submenu actions can still be invoked when no resource is selected and we are not subscribed to all logs, which gets into the filename path that assumes a selected resource.
| } | ||
|
|
||
| private static readonly char[] s_csvSpecialChars = [',', '"', '\r', '\n']; | ||
| private static readonly char[] s_formulaInjectionChars = ['=', '+', '-', '@', '\t', '\r']; |
There was a problem hiding this comment.
I’m going to include \n in this guard and add a regression test. A field that starts with LF before formula-looking content can still be opened by spreadsheet tools as formula content after line normalization.
davidfowl
left a comment
There was a problem hiding this comment.
Performance review: 2 findings.
| var stream = new MemoryStream(); | ||
| lock (_updateLogsLock) | ||
| { | ||
| LogEntrySerializer.WriteLogEntriesToCsvStream(_logEntries.GetEntries(), stream); |
There was a problem hiding this comment.
This serializes the entire CSV while holding _updateLogsLock, which is also the lock used by StartLogEntryChannelReaderTask for live log ingestion. For a large configured log buffer, the export can block incoming log batches/UI updates for all timestamp stripping, CSV escaping, and stream writes. Consider snapshotting the entries under the lock and doing the CSV serialization after releasing it.
| writer.Write(','); | ||
| writer.Write(EscapeCsvField(timestamp)); | ||
| writer.Write(','); | ||
| writer.Write(EscapeCsvField(entry.GetStrippedLogContent() ?? string.Empty)); |
There was a problem hiding this comment.
GetStrippedLogContent() memoizes the stripped message on the LogEntry. A CSV download is a one-off export, but this call populates _strippedLogContent for every retained row and keeps another full message string alive until the entry is evicted or logs are cleared. Consider using a non-caching strip path for export so downloading CSV doesn't permanently increase the dashboard's log-buffer working set.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
b4eabf6 to
6686271
Compare
…console-logs # Conflicts: # src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs
…gs' into feature/csv-export-console-logs # Conflicts: # src/Aspire.Dashboard/Components/Pages/ConsoleLogs.razor.cs # src/Shared/ConsoleLogs/LogEntrySerializer.cs # tests/Aspire.Dashboard.Tests/ConsoleLogsTests/LogEntrySerializerTests.cs
| : string.Join("_", PageViewModel.SelectedResource.Id!.InstanceId!.Split(Path.GetInvalidFileNameChars())); | ||
|
|
||
| return $"{fileNamePrefix}-{TimeProvider.GetLocalNow().ToString("yyyyMMddhhmmss", CultureInfo.InvariantCulture)}.txt"; | ||
| return $"{fileNamePrefix}-{TimeProvider.GetLocalNow().ToString("yyyyMMddhhmmss", CultureInfo.InvariantCulture)}.{extension}"; |
There was a problem hiding this comment.
One small thing I found while testing this path: this uses hh, so the generated filenames are 12-hour timestamps without AM/PM. That can collide or be ambiguous between morning and evening exports. I'll switch it to HH while we're already touching this filename helper.
Use a 24-hour timestamp in console log export filenames so afternoon exports do not collide with morning exports. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Description
Added a "Download as CSV" option to the console logs page alongside the existing "Download as text" option, both nested under a "Download" submenu.
Changes:
Added WriteLogEntriesToCsvStream to LogEntrySerializer with RFC 4180 escaping. CSV columns: Resource, Timestamp, Message
Split single download menu item into a submenu with "Download as text" and "Download as CSV" options
Added localized strings in ConsoleLogs.resx / ConsoleLogs.Designer.cs
XLF files auto-synced via XliffTasks (state="new", expected)
Added 7 unit tests for CSV serialization covering header, columns, escaping, pause skipping
Fixes #11121
Checklist
<remarks />and<code />elements on your triple slash comments?