feat(logs): rewrite logs page with server-side search - #1465
Conversation
af53499 to
ac9b784
Compare
- Derive stable, content-sensitive query key from level Set (Set serializes
to {} in the hash, so level filters never refetched during search)
- Omit empty message/level params instead of sending empty DSL operands
- Skip DSL wrapping for empty search strings
- Fix DSL prefix regex to match server grammar (mode char first, at most
one ! and one # modifier in either order)
- Gate full-screen spinner to initial fetch only; drop redundant isPending
- Replace render-phase side effect + useMemo debounce with an effect
keyed on the trailing virtual index (React Compiler rule compliance)
- Format timestamps once in LogRow; remove formatStamp/formatTimestamps
from the data layer
- Route level-chip filtering to the server search view; drop unused
activeLevels/onClearFilters props from LogLiveView
- Extract virtualizer scrollRect workaround into
useVirtualizerScrollRectWorkaround hook
- Fix hasMore off-by-one before first load (!= null)
- Sync filtersActive with debounced search value
The old detection compared scrollTop snapshots 50ms apart, so the virtualizer's measurement corrections (rows measured != 36px estimate) and programmatic scrollToIndex events looked like a user scroll-up and disabled the lock as soon as logs loaded. Replace it with a bottom check: while locked, the container is only not at the bottom if the user scrolled up. Also catches scrollbar drags and drops the throttle machinery.
PR Review: #1465 — feat(logs): rewrite logs page with server-side search📋 SummaryPR 1465 splits the Logs page into a pure SignalR live tail ( 🚨 Critical Issues & Security (Blockers)None detected. No secrets, no unsafe HTML injection ( 🏗️ Architecture & Pattern Checks
💡 Suggestions & Refactoring1. Align // Before
getNextPageParam: lastPage => lastPage.NextOffset,// After
getNextPageParam: lastPage => lastPage.NextOffset ?? undefined,and prefer the hook's own 2. Add a regression test for the DSL prefix matcher. 3. UX consistency (not a bug): live tail is chronological-ascending; search results are 🔍 Nitpicks
|
|
Closes #221 |
Summary
Rewrites the Logs page: the live tail (
LogLiveView) is now a pure SignalR feed, and a newLogSearchViewadds full-history server-side search viaLogging/Range/Readwith infinite scroll. The React Query log module moves fromreact-query/logs/toreact-query/logging/and the log event type is expanded (logger/caller/exception).Changes
Added
LogSearchView.tsx— debounced search text and/or level chips switch the page from the live tail to full-history search, with virtualized infinite scroll (effect-triggeredfetchNextPage). Search text supports the server's filter DSL:c:foo=:foo^:foo$:foo~:foo*:foo/pattern/ifor ignore-case)!(negate) and#(ignore case) can be appended after the mode in either order (c!#:foo, each once). Bare text is a case-insensitive contains; inputs that don't match the DSL grammar are wrapped asc#:<text>so the server never rejects them.LogLevelChip.tsx,LogRow.tsx— level filter chips and a shared log row (timestamps formatted exactly once, here).useVirtualizerScrollRectWorkaround.ts— shared hook wrapping the TanStack Virtual #634 measurement fix (Firefox/Chrome), replacing duplicated inline code in both views.Fixed
LogLiveView.tsx— scroll-to-bottom lock no longer disables itself while logs load: the oldscrollTopsnapshot race was tripped by the virtualizer's measurement corrections; it now checks distance-from-bottom instead (scrollbar drags are caught too).queries.ts— query keys derive a stable, content-sensitive string from the levelSet(aSetserializes to{}in the key hash, so level changes previously didn't refetch); emptymessage/levelparams are omitted from requests rather than sent as empty strings.Removed
react-query/logs/— replaced byreact-query/logging/with expandedLogEventType(logger/caller/exception);LogLineTypedropped fromcommon.ts.Screenshots
Live tail
Level filter (server-side search)
Search