Skip to content

feat(browser): Emit low-cardinality ui span names - #24578

Open
Lms24 wants to merge 8 commits into
developfrom
feat/ui-span-name-conventions
Open

Lms24 wants to merge 8 commits into
developfrom
feat/ui-span-name-conventions

Conversation

@Lms24

@Lms24 Lms24 commented Sep 22, 2026

Copy link
Copy Markdown
Member

Emits low cardinality span names for ui spans when span streaming is enabled. This includes:

  • web vital spans (LCP, CLS, INP): component name or hard-coded string name (unchanged description)
  • component rendering spans (mount, render, update, etc): component name or hard-coded string name (unchanged description)
  • task/loaf spans: hard-coded string name, description unchanged
  • interaction spans: parameterized route or hard-coded string name, desc. unchanged

mostly, we just override description inference here because we can't really model the descriptions with just attributes/templates in a general enough way. I think this is acceptable for these spans.

conventions are still pending: getsentry/sentry-conventions#643, so I'll only merge this once conventions are approved

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 98884b0. Configure here.

Comment thread packages/browser-utils/src/performance/interactions.ts
Comment thread packages/browser-utils/src/web-vitals/spans.ts
@github-actions

github-actions Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 29.16 kB - -
@sentry/browser - with treeshaking flags 27.42 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.33 kB - -
@sentry/browser (incl. Tracing) 51.09 kB +0.27% +134 B 🔺
@sentry/browser (incl. Tracing + Span Streaming) 51.1 kB +0.3% +148 B 🔺
@sentry/browser (incl. Tracing, Profiling) 54.1 kB +0.24% +128 B 🔺
@sentry/browser (incl. Tracing, Replay) 90.67 kB +0.15% +133 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.78 kB +0.16% +123 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 95.38 kB +0.14% +127 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 108.35 kB +0.15% +157 B 🔺
@sentry/browser (incl. Feedback) 46.69 kB - -
@sentry/browser (incl. sendFeedback) 34.22 kB - -
@sentry/browser (incl. FeedbackAsync) 39.32 kB - -
@sentry/browser (incl. Metrics) 30.18 kB - -
@sentry/browser (incl. Logs) 30.43 kB - -
@sentry/browser (incl. Metrics & Logs) 31.09 kB - -
@sentry/react 30.91 kB - -
@sentry/react (incl. Tracing) 53.37 kB +0.26% +137 B 🔺
@sentry/vue 36.65 kB +0.37% +132 B 🔺
@sentry/vue (incl. Tracing) 53.62 kB +0.52% +277 B 🔺
@sentry/svelte 29.18 kB - -
CDN Bundle 30.86 kB - -
CDN Bundle (incl. Tracing) 51.62 kB +0.3% +152 B 🔺
CDN Bundle (incl. Logs, Metrics) 33.12 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 53.58 kB +0.25% +132 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) 73.84 kB - -
CDN Bundle (incl. Tracing, Replay) 89.2 kB +0.2% +177 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 91.16 kB +0.19% +167 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 95.35 kB +0.18% +168 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 97.32 kB +0.18% +173 B 🔺
CDN Bundle - uncompressed 91.25 kB - -
CDN Bundle (incl. Tracing) - uncompressed 153.62 kB +0.19% +286 B 🔺
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.82 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 159.57 kB +0.18% +286 B 🔺
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 227.39 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 273.35 kB +0.11% +286 B 🔺
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 279.28 kB +0.11% +286 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 287.05 kB +0.1% +286 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 292.98 kB +0.1% +286 B 🔺
@sentry/nextjs (client) 55.71 kB +0.26% +144 B 🔺
@sentry/sveltekit (client) 51.52 kB +0.27% +135 B 🔺
@sentry/core/server 39.92 kB - -
@sentry/core/browser 13.63 kB - -
@sentry/node 133.59 kB +0.02% +17 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 82.4 kB - -
@sentry/node - without tracing 90.15 kB +0.03% +24 B 🔺
@sentry/node - without channel injection 112.11 kB +0.02% +15 B 🔺
@sentry/aws-serverless 98.41 kB +0.03% +26 B 🔺
@sentry/cloudflare (withSentry) - minified 205.48 kB - -
@sentry/cloudflare (withSentry) 511.24 kB - -

View base workflow run

@Lms24 Lms24 changed the title feat: Port UI span names to low-cardinality conventions feat(browser): Emit low-cardinality ui span names Sep 22, 2026
@Lms24
Lms24 force-pushed the feat/ui-span-name-conventions branch from 215d521 to 1defdc9 Compare September 22, 2026 14:49
const pageloadSpanId = inpSpan.parent_span_id;

expect(inpSpan).toEqual({
name: 'body > NormalButton',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this is fine IMHO because for this test (plus the ones below), trace lifecycle is static but we nevertheless emit inp as a v2 span. So the naming change doesn't have to apply here (logaf l though but we can fix later if it comes up at all)

[SENTRY_SEGMENT_NAME]: {
type: 'string',
value: 'Pageload',
value: 'Click',

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

this fixes a bug previously, since we took the "Pageload" low card. name from the active pageload span previously. "Click" is better than that IMHO

@Lms24 Lms24 self-assigned this Sep 22, 2026
@Lms24
Lms24 marked this pull request as ready for review September 22, 2026 15:34
@Lms24
Lms24 requested review from a team as code owners September 22, 2026 15:34
@Lms24
Lms24 requested review from logaretm, msonnb, nicohrubec and s1gr1d and removed request for a team September 22, 2026 15:34
@Lms24

Lms24 commented Sep 22, 2026

Copy link
Copy Markdown
Member Author

yo bugbot review

@logaretm logaretm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think I prefer to align with the API's own naming rather than consolidate them.

I explain a bit in this PR:

getsentry/sentry-conventions#641 (comment)

@logaretm logaretm left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

A bit of a nit, handling unknown the case of the INP target is interesting.

in #24573 which overlaps a bit with this (it's fine it also adds a new attribute so no worries on the inp attr), but in that PR i chose to omit.

Question for the review: should the "unknown" case be reported as or as unknown without the brackets, or omit it entirely? I chose to omit it for now.

Thoughts on this? I don't have a strong opinion here, I listed all 3 options in that PR.

Comment thread packages/angular/src/tracing.ts
[SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME]: entry?.duration ?? inpValue,
};
if (selector) {
attributes['browser.web_vital.inp.target'] = selector;

@logaretm logaretm Sep 22, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

m: We got some risk here, name value changed to be the component name so it would mess up what develop does after #24573 was merged.

So to preserve the values correctly we need to:

  • Drop the if (selector) check block to preserve unknown handling.
  • Replace name down there with selector to make sure INP targets still has the selector.
entry && selector !== UNKNOWN_ELEMENT_NAME && (attributes['browser.web_vital.inp.target'] = selector);

Otherwise LGTM!

Lms24 and others added 7 commits September 22, 2026 21:35
When span streaming is enabled, UI component, interaction, action, and
web-vital spans use low-cardinality names instead of DOM paths or
angle-bracketed component names.

Co-Authored-By: Cursor <cursoragent@cursor.com>
The E2E suites still expected the pre-convention span shape. Streamed INP
spans on unannotated elements are named `Click` now, with the DOM path on
`ui.element.target`, and component spans carry `ui.component_name` in both
trace lifecycles, which broke the static apps asserting an exact `data` map.

Also assert the spotlight filter against `ui.element.target` directly instead
of falling back to the span name, so the test pins one shape rather than
passing either way.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The suites checked the new low-cardinality span names but not the attributes
that now carry what the names gave up, so a regression that dropped
`ui.component_name` or `sentry.description` would have gone unnoticed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ebase

Rebasing onto develop replayed this branch over "Report the INP target and
interaction type as attributes" (#24573). Both sides had added an attributes
block to `_sendInpSpan`, and the auto-merge kept both, leaving a duplicate
`const attributes` declaration and a duplicate `browser.web_vital.inp.target`
assignment. Git raised no conflict for either.

Keep that PR's attribute line and drop ours, with one change: it read the
selector off the span name, which no longer holds one here. Under span
streaming the name is the component name or the op's fallback, so the
attribute now reads `selector` directly. Omitting it for a missing entry or an
unresolved element is unchanged.

The same merge duplicated the attribute's key inside single object literals
across six test files, which is dead code in a JS object. Drop the second of
each pair, and drop our negative assertion for the entry-less case, which that
PR's own test covers more precisely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Lms24
Lms24 force-pushed the feat/ui-span-name-conventions branch from 4f50163 to c1a59f1 Compare September 22, 2026 19:55
Comment thread packages/browser-utils/src/performance/interactions.ts Outdated
`browser.web_vital.inp.target` is new on `ui.interaction.click` spans, since the
selector no longer fits in the low-cardinality span name. It was set from
`htmlTreeAsString(entry.target)` unconditionally, which yields `<unknown>` for a
null target and an empty string for a node without a tag name.

`PerformanceEventTiming.target` is null whenever the node is disconnected or in a
shadow tree, so a handler that removes its own element, or any click inside a web
component, would have tagged the span with a value nobody can filter on. Guard
the attribute the way `_sendInpSpan` already does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread packages/browser-utils/src/web-vitals/spans.ts

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants