Keep the log view following and readable: network-share tailing, bulk row-cap trim, selection contrast #9

Merged
hannah-vernon merged 3 commits from fix/network-share-tailing into dev 2026-07-03 15:32:08 -05:00

What does this PR do?

Three fixes that all keep the combined log view showing new lines reliably and legibly.

1. Detect appended data on network shares

Tailing could stop showing new lines on a UNC / SMB share because new-content detection relied on FileStream.Length, which the SMB redirector can cache as a stale (smaller) value, so the tailer concluded there was nothing new even while the file kept growing. A restart did not recover it because the stale length lives in the OS redirector, not in the app.

  • FileTailer.ReadNew now seeks to the last offset and reads through to the real end of stream (reads until a zero-byte read) instead of bounding reads by the cached fs.Length. fs.Length is now used only for truncation / rotation detection.
  • FolderTailer raises a DiscoveryCompleted event after discovery (so the UI can show "Following" and the matched-file count before the first new line), handles FileSystemWatcher.Error (buffer overflow / dropped watch) by forcing a rescan while the periodic poll carries on, and bumps InternalBufferSize to 64 KB.
  • Status bar gains the matched-file count, the follow state as soon as tailing starts (no longer stuck on "Starting..."), a "last new HH:mm:ss" time so a stalled source is visible, and a persistent last-notice.

2. Trim the row cap in bulk

Once the view reached its row cap (MaxRows, 50,000 by default), each poll trimmed the overflow with a per-row ObservableCollection.RemoveAt(0) loop. Every removal raised its own collection-changed event, each driving a DataGrid layout pass; at the cap under a fast log rate that is thousands of events per poll, which saturated the UI thread and made the display appear to stop showing new lines.

  • New BoundedLogCollection.TrimHead(max) drops the oldest rows via a single List.RemoveRange and one reset notification instead of one event per row.
  • AppendRows lets the buffer grow a little past the cap then bulk-trims, so a trim (and any scroll reposition when auto-scroll is off) happens rarely; additions stay incremental so appending does not disturb the scroll position.

3. Keep selected rows readable

With full-row selection the default theme left the log text dark on a pale highlight (fainter still when the grid is unfocused), so a clicked row lost contrast; the transparent compact cells let that show through. An IsSelected trigger now paints selected cells white-on-blue whether or not the grid is focused.

How was this tested?

  • dotnet build Tayler.slnx -c Debug - 0 errors, 0 warnings
  • Network-share fix: local harness confirmed existing lines plus all after-start appends are displayed, with the status bar reporting "Following", the file count, and the last-new time.
  • Cap fix: ran with MaxRows=20 and appended 200 lines quickly - the view stayed responsive, trimmed to the newest 20 rows, and showed "20 rows - Following".
  • Selection contrast: launched the app, clicked a row, confirmed white text on a solid blue highlight.

Checklist

  • I have read the Contributing Guide
  • Changes are focused - one logical change per PR
  • Documentation updated (if user-visible behavior changed)
  • New dependencies are MIT/Apache-2.0 and security-vetted
  • No commented-out code or debug leftovers
## What does this PR do? Three fixes that all keep the combined log view showing new lines reliably and legibly. ### 1. Detect appended data on network shares Tailing could stop showing new lines on a UNC / SMB share because new-content detection relied on `FileStream.Length`, which the SMB redirector can cache as a stale (smaller) value, so the tailer concluded there was nothing new even while the file kept growing. A restart did not recover it because the stale length lives in the OS redirector, not in the app. - `FileTailer.ReadNew` now seeks to the last offset and reads through to the real end of stream (reads until a zero-byte read) instead of bounding reads by the cached `fs.Length`. `fs.Length` is now used only for truncation / rotation detection. - `FolderTailer` raises a `DiscoveryCompleted` event after discovery (so the UI can show "Following" and the matched-file count before the first new line), handles `FileSystemWatcher.Error` (buffer overflow / dropped watch) by forcing a rescan while the periodic poll carries on, and bumps `InternalBufferSize` to 64 KB. - Status bar gains the matched-file count, the follow state as soon as tailing starts (no longer stuck on "Starting..."), a "last new HH:mm:ss" time so a stalled source is visible, and a persistent last-notice. ### 2. Trim the row cap in bulk Once the view reached its row cap (`MaxRows`, 50,000 by default), each poll trimmed the overflow with a per-row `ObservableCollection.RemoveAt(0)` loop. Every removal raised its own collection-changed event, each driving a `DataGrid` layout pass; at the cap under a fast log rate that is thousands of events per poll, which saturated the UI thread and made the display appear to stop showing new lines. - New `BoundedLogCollection.TrimHead(max)` drops the oldest rows via a single `List.RemoveRange` and one reset notification instead of one event per row. - `AppendRows` lets the buffer grow a little past the cap then bulk-trims, so a trim (and any scroll reposition when auto-scroll is off) happens rarely; additions stay incremental so appending does not disturb the scroll position. ### 3. Keep selected rows readable With full-row selection the default theme left the log text dark on a pale highlight (fainter still when the grid is unfocused), so a clicked row lost contrast; the transparent compact cells let that show through. An `IsSelected` trigger now paints selected cells white-on-blue whether or not the grid is focused. ## How was this tested? - [x] `dotnet build Tayler.slnx -c Debug` - 0 errors, 0 warnings - [x] Network-share fix: local harness confirmed existing lines plus all after-start appends are displayed, with the status bar reporting "Following", the file count, and the last-new time. - [x] Cap fix: ran with `MaxRows=20` and appended 200 lines quickly - the view stayed responsive, trimmed to the newest 20 rows, and showed "20 rows - Following". - [x] Selection contrast: launched the app, clicked a row, confirmed white text on a solid blue highlight. ## Checklist - [x] I have read the [Contributing Guide](../CONTRIBUTING.md) - [x] Changes are focused - one logical change per PR - [x] Documentation updated (if user-visible behavior changed) - [x] New dependencies are MIT/Apache-2.0 and security-vetted - [x] No commented-out code or debug leftovers
Tailing could stop showing new lines on UNC / SMB shares because new-content
detection relied on FileStream.Length, which the SMB redirector can cache as a
stale (smaller) value, so the tailer concluded there was nothing new even while
the file kept growing. A restart did not recover it because the stale length
lives in the OS redirector, not in the app.

- FileTailer.ReadNew: seek to the last offset and read through to the real end of
  stream (read until a zero-byte read) instead of bounding reads by the cached
  fs.Length. fs.Length is now used only for truncation / rotation detection. A
  read at the offset goes to the server and returns genuinely appended bytes.
- FolderTailer: raise a DiscoveryCompleted event after discovery so the UI can
  show "Following" and the matched-file count even before the first new line;
  handle FileSystemWatcher.Error (internal-buffer overflow / dropped watch) by
  forcing a rescan and notice while the periodic poll carries on; bump the
  watcher InternalBufferSize to 64 KB.
- FolderWindow: status bar now shows matched-file count, the follow state as soon
  as tailing starts (no longer stuck on "Starting..."), the "last new HH:mm:ss"
  time so a stalled source is visible, and the most recent notice persistently
  instead of a one-shot message.
- README: document the network-share behaviour and the richer status bar.

Verified with a local harness: existing lines plus all after-start appends are
displayed, and the status bar reports "Following", file count, and last-new time.
Build is 0 warnings / 0 errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Once the combined view reached its row cap (MaxRows, 50,000 by default), each
poll trimmed the overflow with a per-row ObservableCollection.RemoveAt(0) loop.
Every removal shifted the whole backing array and, more importantly, raised its
own collection-changed event, each of which drove a layout pass in the bound
DataGrid. At the cap under a fast log rate that is thousands of events per 250 ms
poll, which saturated the UI thread and made the display appear to stop showing
new lines.

- Add BoundedLogCollection (ObservableCollection<LogRow>) with TrimHead(max),
  which drops the oldest rows directly on the backing list (a single
  List.RemoveRange) and raises one Reset notification instead of one event per
  row.
- FolderWindow.AppendRows now lets the buffer grow a small amount past the cap
  and then bulk-trims, so a trim (and any scroll reposition when auto-scroll is
  off) happens rarely rather than on every batch. Additions stay incremental so
  appending does not disturb the scroll position.
- README: document the bounded-view / bulk-trim behaviour.

Verified by running the app with MaxRows=20 and appending 200 lines quickly: the
view stayed responsive, trimmed to the newest 20 rows, and the status bar showed
"20 rows - Following". Build is 0 warnings / 0 errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Clicking a row in the combined view was hard to read: with full-row selection
the default WPF theme leaves the log text dark and paints a pale highlight behind
it (and an even fainter brush when the grid is not focused), so the selected line
lost contrast. Because the cells are transparent for the compact look, that
washed-out selection showed through.

Add an IsSelected trigger to the DataGridCell style that paints selected cells
with white text on a solid blue background, applied whether or not the grid has
focus, so a selected row stays legible.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
hannah-vernon changed title from Fix log tailing stalling on network shares; richer status bar to Keep the log view following and readable: network-share tailing, bulk row-cap trim, selection contrast 2026-07-03 15:31:54 -05:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
hannah-vernon/tayler-log-tailer!9
No description provided.