feat: adds log explorer component#2283
Conversation
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
| <Section borderColor={variant === 'ghost' ? 'transparent' : undefined} minWidth="800px" overflowY="auto"> | ||
| <div className="mb-6 flex items-center justify-between gap-2"> |
There was a problem hiding this comment.
adding variant so we can embed legacy version when feature flag for new log explorer is off
| function isFilterMatch(str: string, m: TopicMessage) { | ||
| const lowerStr = str.toLowerCase(); | ||
| if (m.offset.toString().toLowerCase().includes(lowerStr)) { | ||
| return true; | ||
| } | ||
| if (m.keyJson?.toLowerCase().includes(lowerStr)) { | ||
| return true; | ||
| } | ||
| if (m.valueJson?.toLowerCase().includes(lowerStr)) { | ||
| return true; | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
extracted to shared util
| function trimSlidingWindow({ | ||
| messages, | ||
| maxResults, | ||
| pageSize, | ||
| currentGlobalPage, | ||
| windowStartPage, | ||
| virtualStartIndex, | ||
| }: { | ||
| messages: TopicMessage[]; | ||
| maxResults: number; | ||
| pageSize: number; | ||
| currentGlobalPage: number; | ||
| windowStartPage: number; | ||
| virtualStartIndex: number; | ||
| }): { messages: TopicMessage[]; windowStartPage: number; virtualStartIndex: number; trimCount: number } { | ||
| const maxWindowSize = maxResults + pageSize; | ||
|
|
||
| if (maxResults < pageSize || messages.length <= maxWindowSize) { | ||
| return { messages, windowStartPage, virtualStartIndex, trimCount: 0 }; | ||
| } | ||
|
|
||
| const excess = messages.length - maxWindowSize; | ||
| const currentLocalPage = Math.max(0, currentGlobalPage - windowStartPage); | ||
|
|
||
| // Never trim the page the user is currently viewing or the one before it | ||
| const maxPagesToTrim = Math.max(0, currentLocalPage - 1); | ||
| const pagesToTrim = Math.min(Math.floor(excess / pageSize), maxPagesToTrim); | ||
| const trimCount = pagesToTrim * pageSize; | ||
|
|
||
| if (trimCount === 0) { | ||
| return { messages, windowStartPage, virtualStartIndex, trimCount: 0 }; | ||
| } | ||
|
|
||
| return { | ||
| messages: messages.slice(trimCount), | ||
| windowStartPage: windowStartPage + pagesToTrim, | ||
| virtualStartIndex: virtualStartIndex + trimCount, | ||
| trimCount, | ||
| }; | ||
| } | ||
|
|
There was a problem hiding this comment.
extracted to shared util
|
@claude review |
|
Claude finished @malinskibeniamin's task in 2m 2s —— View job Code Review
Overall this is a well-structured extraction and new molecule. The 59 unit/integration tests provide solid coverage. A few issues found below, ranging from an accessibility bug to minor code quality items. Bugs1. Label-for/id mismatch — clicking "Live" label won't toggle the switch
// Current (broken)
<Switch checked={liveViewEnabled} onCheckedChange={...} /> {/* no id */}
<Label htmlFor="live-view-toggle">Live</Label>
<InfoIcon id="live-view-toggle" /> {/* wrong element gets the id */}
// Fix: move id to Switch
<Switch id="live-view-toggle" checked={liveViewEnabled} onCheckedChange={...} />
<Label htmlFor="live-view-toggle">Live</Label>2.
Design Concerns3.
4. Live mode has unbounded memory growth per session
setMessages((prev) => [...prev, msg]);With 5. Echoing existing reviewer feedback (magic numbers and BigInt timestamps) These are already raised by @malinskibeniamin in inline comments — agreeing with both:
Minor Code Quality6.
const textDecoder = new TextDecoder();7. Magic number
8. Fragile CSS class selector in test
Reviewer Action Items Verification✅ ✅ ✅ |
…in work for live mode
|
Claude finished @eblairmckee's task in 1m 14s —— View job Researching proto3 timestamp utilities
Answer:
|
|
Claude finished @eblairmckee's task in 6m 25s —— View job Analysis: last 2 commits (merge master + MobX fix)
SummaryThe fix in What the merge broke (
|
|
#2250 changes for reference: |

Summary
LogExplorercomponent (components/ui/connect/log-explorer.tsx) — pipeline log viewer built with TanStack Table, featuring column filtering (level, component, message text), sorting, pagination, and a detail sheet for individual log entries.react-query/api/logs.tsx— log search hook using two strategies:useTanstackQuery) with incremental streaming via a ref + flush interval, so the table populates progressively during the search and query cache handles refetch/staleness.listMessagesdirectly, prepending new messages (newest-first tail mode).filterInterpreterCode) with client-sidekeyJsonfallback on serverless.backend-api.ts:message-converters.ts—convertListMessageData(message deserialization/conversion), previously inline increateMessageSearch. Now shared by both the legacycreateMessageSearchand the newuseLogSearch.message-table-helpers.ts—isFilterMatch(extracted frompipelines-details.tsx) andtrimSlidingWindow(extracted fromTab.Messages/index.tsx). Pure functions, zero side-effect imports.enableNewPipelineLogscontrols whether the pipeline view tab rendersLogExplorer(new) orLogsTab(legacy). Defaults tofalse.LogsTabimprovements: accepts avariantprop for border styling, uses registryButtonfor the refresh action, and imports sharedisFilterMatchinstead of defining it locally.Test plan
message-converters.test.ts— unit tests for message deserialization (compression types, encodings, troubleshooting, payload edge cases)message-table-helpers.test.ts— unit tests forisFilterMatchandtrimSlidingWindowlog-explorer.test.tsx— integration tests for the component (loading, empty state, table rendering, pagination, log levels, error display, refresh, detail sheet)logs.test.tsx— integration test verifying live mode prepends messages (newest first)Live view
Historical view with filters