Skip to content

feat: trace waterfall redesign#2565

Open
karl-power wants to merge 5 commits into
mainfrom
hdx-3945-3952-trace-waterfall-redesign
Open

feat: trace waterfall redesign#2565
karl-power wants to merge 5 commits into
mainfrom
hdx-3945-3952-trace-waterfall-redesign

Conversation

@karl-power

@karl-power karl-power commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Redesigns the trace waterfall chart and cleans up the surrounding trace panel layout.

Waterfall redesign (HDX-3945)

  • Per-service colors: each service gets a stable color (sorted for render-order stability). Green is reserved for correlated log rows and red for error spans, so a service color is never confused with a log or error.
  • Bar & label layout: no text inside bars — the operation/service label sits beside a vertical service color bar, with the span duration shown as muted text and the span body revealed on hover, placed on whichever side has more room.
  • Depth controls: expand/collapse one level and expand/collapse all, only shown when the trace has collapsible nodes.

Trace panel layout (HDX-3952)

  • Removed the duplicated Trace ID from the panel body (it lives in the side panel header). The Trace ID Expression editor now only appears as a fallback when no trace id resolves for the event.
  • Moved the Correlated Logs source selector into the waterfall controls bar as a compact selector.

Collapse hint bug (HDX-4444)

  • Removed the flaky tooltip-based Alt+click collapse hint (CollapseTooltipLabel / useDisclosure) that could render multiple times when chevrons were highlighted in quick succession. Collapse is now an always-present chevron control.

Supporting changes

  • useWaterfallSearchState now persists the filter language (whereLanguage) in the URL so shared links / reloads rebuild the query in the language it was written in.
  • SearchWhereInput accepts a parentRef for popover positioning.

Testing

  • Added DBTraceWaterfallChart.test.tsx covering the depth-control logic.
  • Updated DBTracePanel.test.tsx for the new layout.

Screenshots or video

Screenshot 2026-07-01 at 17 12 42

How to test on Vercel preview

Preview routes: /search

Steps:

  1. Open the trace timeline and explore the new features described above.

References

@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5107605

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@hyperdx/app Patch
@hyperdx/api Patch
@hyperdx/otel-collector Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
hyperdx-oss Ready Ready Preview, Comment Jul 8, 2026 9:13am
hyperdx-storybook Ready Ready Preview, Comment Jul 8, 2026 9:13am

Request Review

@github-actions github-actions Bot added the review/tier-3 Standard — full human review required label Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

🟡 Tier 3 — Standard

Introduces new logic, modifies core functionality, or touches areas with non-trivial risk.

Why this tier:

  • Diff size: 997 production lines changed (Tier 2 max: < 250)

Review process: Full human review — logic, architecture, edge cases.
SLA: First-pass feedback within 1 business day.

Stats
  • Production files changed: 10
  • Production lines changed: 997 (+ 482 in test files, excluded from tier calculation)
  • Branch: hdx-3945-3952-trace-waterfall-redesign
  • Author: karl-power

To override this classification, remove the review/tier-3 label and apply a different review/tier-* label. Manual overrides are preserved on subsequent pushes.

@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR redesigns the trace waterfall chart with per-service colors, a revised label/bar layout, and depth-level collapse controls; it also cleans up the surrounding trace panel by moving the correlated-log source selector into the controls bar and removing the duplicated trace ID.

  • Waterfall redesign: per-service stable colors (green/red reserved for logs/errors), vertical service color bar beside the label, duration and on-hover body shown outside the bar, and new expand/collapse depth controls backed by thoroughly tested pure helpers (computeExpandOneLevel, computeCollapseOneLevel, etc.).
  • Filter resilience: useFilteredEventsAroundFocus wraps every query with a fallback that retries without the filter on failure, surfacing a non-fatal inline error rather than blanking the chart — both filter-level and source-level errors handled separately for trace and log sides.
  • URL state for filter language: useWaterfallSearchState now persists whereLanguage in the URL so shared links and reloads reconstruct the query in the correct language, fixing the previously noted UI/execution language mismatch.

Confidence Score: 5/5

Safe to merge — the redesign is well-scoped, the new filter-error fallback prevents chart blanking on bad filters, and the depth-control helpers are covered by thorough unit tests.

All the new logic paths are correctly handled: the dual-query fallback in useFilteredEventsAroundFocus correctly isolates filter failures from fatal errors, the URL-first language seeding resolves the previously noted shared-link inconsistency, and the collapse/expand helpers are pure functions with good test coverage. The one concern — parentIdsByLevel being recomputed on every collapse action — is a performance trade-off, not a correctness issue, and would only be noticeable on very large traces.

DBTraceWaterfallChart.tsx — the parentIdsByLevel computation shares a useMemo with flattenedNodes; worth revisiting if collapse interactions feel sluggish on large traces.

Important Files Changed

Filename Overview
packages/app/src/components/DBTraceWaterfallChart.tsx Core waterfall redesign: service color mapping, depth-control helpers, filter-error fallback hook, and SearchWhereInput upgrade. One performance concern: parentIdsByLevel is computed inside the same useMemo as flattenedNodes, so it is fully rebuilt on every expand/collapse action.
packages/app/src/components/TimelineChart/TimelineXAxis.tsx Splits the single tick container into a grid layer (full-height background lines, no z-index) and a label layer (sticky header). Both containers share the same spacing computation. Clean and correct.
packages/app/src/components/TimelineChart/TimelineChartRowEvents.tsx Removes text from inside the bar and moves duration + on-hover span body outside via the new barDetail/barDetailBody CSS classes. Direction logic (left vs right) is correct.
packages/app/src/hooks/useWaterfallSearchState.tsx Adds traceWhereLanguage and logWhereLanguage to URL query state; clear() now nulls both language params. Correct and complete.
packages/app/src/components/DBTracePanel.tsx Removes the duplicated trace ID row and the inline edit flow; moves the correlated-log source selector into controlsExtra for the waterfall controls bar. Clean simplification.
packages/app/src/components/tests/DBTraceWaterfallChart.test.tsx Extensive new test coverage: depth-control helpers, filter-error fallback behaviour (filter vs fatal), per-source input rendering, and a content-keyed query mock that is more stable than the previous call-order mock.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[useFilteredEventsAroundFocus] --> B[Run filtered query\nhiddenRowExpression set]
    B --> C{Query error?}
    C -- No --> D[Return filtered rows\nfatalError: undefined\nfilterError: undefined]
    C -- Yes --> E{Is filterFailed?\nenabled && expr && error}
    E -- Yes --> F[Run fallback query\nhiddenRowExpression: undefined]
    F --> G{Fallback error?}
    G -- No --> H[Return unfiltered rows\nfilterError: filtered.error\nfatalError: undefined]
    G -- Yes --> I[Return undefined rows\nfilterError: undefined\nfatalError: fallback.error]
    E -- No --> J[Return undefined rows\nfatalError: filtered.error]
    H --> K[Inline filter error shown\nbelow filter input]
    I --> L[Full-chart error block]
    J --> L
    D --> M[Chart renders normally]
    H --> M
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A[useFilteredEventsAroundFocus] --> B[Run filtered query\nhiddenRowExpression set]
    B --> C{Query error?}
    C -- No --> D[Return filtered rows\nfatalError: undefined\nfilterError: undefined]
    C -- Yes --> E{Is filterFailed?\nenabled && expr && error}
    E -- Yes --> F[Run fallback query\nhiddenRowExpression: undefined]
    F --> G{Fallback error?}
    G -- No --> H[Return unfiltered rows\nfilterError: filtered.error\nfatalError: undefined]
    G -- Yes --> I[Return undefined rows\nfilterError: undefined\nfatalError: fallback.error]
    E -- No --> J[Return undefined rows\nfatalError: filtered.error]
    H --> K[Inline filter error shown\nbelow filter input]
    I --> L[Full-chart error block]
    J --> L
    D --> M[Chart renders normally]
    H --> M
Loading

Reviews (10): Last reviewed commit: "use separate filter values" | Re-trigger Greptile

Comment thread packages/app/src/components/DBTraceWaterfallChart.tsx
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

E2E Test Results

All tests passed • 225 passed • 3 skipped • 1427s

Status Count
✅ Passed 225
❌ Failed 0
⚠️ Flaky 3
⏭️ Skipped 3

Tests ran across 4 shards in parallel.

View full report →

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Deep Review

Scope: PR #2565 — trace waterfall redesign. Frontend-only (React/TS) diff across DBTraceWaterfallChart.tsx, DBTracePanel.tsx, SearchWhereInput.tsx, TimelineChart/*, useWaterfallSearchState.tsx, and utils.ts (~1180 additions). No auth, DB migration, or API-contract surface.

No critical issues found. No P0/P1 defects surfaced. The filter-language URL persistence is correctly wired end-to-end, the removed getChartColorErrorHighlight / getChartColorWarningHighlight exports were verified unused repo-wide, and the depth-control helpers (computeCollapseAll / computeExpandOneLevel / computeCollapseOneLevel / computeToggleCollapse) are now pure exported functions with unit coverage. The recommendations below are non-blocking.

🟡 P2 -- recommended

  • packages/app/src/components/DBTraceWaterfallChart.tsx:419 -- the new useFilteredEventsAroundFocus filter-failure fallback (re-runs the query unfiltered and surfaces an inline ErrorCollapse while keeping valid spans visible) is meaningful new user-facing behavior with no test coverage.
    • Fix: add a test that forces the filtered query to error and asserts fallback rows render alongside the inline filter-error message for both the spans and logs inputs.
🔵 P3 nitpicks (2)
  • packages/app/src/components/DBTraceWaterfallChart.tsx:972 -- the onLanguageChange handlers call setValue('traceWhereLanguage'/'logWhereLanguage', lang), but SearchWhereInput already updates the same controlled form field via its own languageField.onChange, making the write redundant.
    • Fix: drop the explicit setValue in onLanguageChange unless the shouldDirty side effect is specifically required, and add a comment if it is.
  • packages/app/src/components/DBTracePanel.tsx:373 -- the relocated correlated-logs source selector (and the waterfall depth collapse/expand state) live only in local component/form state, so a shared link cannot reproduce the selected log source or tree shape; this diff expands that control surface without URL parity.
    • Fix: consider persisting the correlated-logs source id (and optionally collapse state) to URL query state as follow-up so shared links reproduce the view; pre-existing pattern, out of scope for this PR.
    • agent-native

Reviewers (2 returned of 10 dispatched): ce-agent-native-reviewer, ce-learnings-researcher. The other 8 dispatched personas (correctness, adversarial, testing, maintainability, project-standards, performance, kieran-typescript, julik-frontend-races) did not return before synthesis; the findings above combine the returned reviewers with direct orchestrator analysis of the full diff. No prior docs/solutions/ learnings apply to this area.

Testing gaps:

  • whereLanguage URL round-trip (shared-link / reload fidelity across sql↔lucene) is not covered.
  • Per-service color-map stability across re-renders (sorted assignment, palette wrap-around when services exceed SERVICE_COLORS.length) is not covered.
  • Duration/body label side-placement (onRight in TimelineChartRowEvents) has no assertion.

@elizabetdev elizabetdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Image

It's looking good! 🎉

From a design perspective we no longer need the left tabs because they moved to the right side panel of the traces view.

Now that we have a top keyboard shortcut action, I don't think we need to show the keyboard shortcut above the spans.

@karl-power

Copy link
Copy Markdown
Contributor Author

It's looking good! 🎉

From a design perspective we no longer need the left tabs because they moved to the right side panel of the traces view.

Now that we have a top keyboard shortcut action, I don't think we need to show the keyboard shortcut above the spans.

Thanks for taking a look @elizabetdev! Both of those points should already be addressed in #2541

The changes here are focused mostly on the waterfall/timeline.

@karl-power karl-power force-pushed the hdx-3945-3952-trace-waterfall-redesign branch 2 times, most recently from eb9103a to c57c581 Compare July 7, 2026 14:12
@teeohhem

teeohhem commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

@karl-power the P0 is a legit issue with the combined filters approach

🔴 P0/P1 — must fix
packages/app/src/components/DBTraceWaterfallChart.tsx:646 — the single filter input is written to both traceWhere and logWhere and both queries now run in the user-selected SQL language, so a span-only-column filter (e.g. the sqlPlaceholder's own StatusCode = 'Error' example) is applied as NOT (...) against the log source, whose table lacks that column; the resulting query error sets logError, and error = traceError || logError replaces the entire waterfall — valid spans included — with a raw error block.
Fix: isolate log-source filter/query errors from the span render path so a log-only failure cannot blank the whole chart, and/or stop mirroring a span-scoped filter verbatim onto the log query.
adversarial, correctness

Before: separate log/trace filter, no issue
image

Combined, if you search for something like SpanName='whatever', you will get this error as SpanName doesn't exist on the logs source.
image

@kodiakhq

kodiakhq Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

This PR currently has a merge conflict. Please resolve this and then re-add the automerge label.

@kodiakhq kodiakhq Bot removed the automerge label Jul 7, 2026
Prefer the URL's whereLanguage param when seeding the filter language
toggle so a shared link / reload shows the same language the query runs
in, and reset the toggle on clear so a stale value isn't re-serialized
into the URL on the next submit. Also drop the now-unused
getChartColorErrorHighlight / getChartColorWarningHighlight exports.
@karl-power

Copy link
Copy Markdown
Contributor Author

@karl-power the P0 is a legit issue with the combined filters approach

Thanks, I missed this. I just updated it now to split the combined filter into separate spans and logs inputs. A failing filter falls back to unfiltered results with the error surfaced inline next to its input.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

automerge review/tier-3 Standard — full human review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants