Skip to content

feat(browser): Replace element timing spans with metrics#19869

Open
logaretm wants to merge 10 commits intodevelopfrom
awad/js-1678-element-timing-metrics-integration
Open

feat(browser): Replace element timing spans with metrics#19869
logaretm wants to merge 10 commits intodevelopfrom
awad/js-1678-element-timing-metrics-integration

Conversation

@logaretm
Copy link
Member

@logaretm logaretm commented Mar 19, 2026

Removes element timing span creation from browserTracingIntegration (deprecates enableElementTiming option, introduces a new standalone elementTimingIntegration that emits Element Timing API data as Sentry distribution metrics instead of spans.

Emits element_timing.render_time and element_timing.load_time metrics with element.identifier and element.paint_type attributes. I believe users can query by the element identifier if they are interested in metrics for a specific element.

Me and Lukas think this is a safe change because it was never documented, even then I made sure to export NO-OP replacement functions to stub them out.

Reasoning for the change

Element Timing values (renderTime, loadTime) are point-in-time timestamps, not durations. Modeling them as spans required awkward workarounds (zero-duration spans, arbitrary start times) that didn't produce meaningful trace data. Metrics are the correct abstraction here.

See discussion in #19261 for full context.

Usage

Sentry.init({
  integrations: [
    Sentry.browserTracingIntegration(),
    Sentry.elementTimingIntegration(),
  ],
});

closes #19260

…ne integration

Remove element timing span creation from browserTracingIntegration and
introduce a new elementTimingIntegration that emits Element Timing API
data as Sentry distribution metrics instead of spans.

Element timing values (renderTime, loadTime) are point-in-time timestamps,
not durations, making metrics a better fit than spans. The new integration
emits `element_timing.render_time` and `element_timing.load_time` metrics
with `element.identifier` and `element.paint_type` attributes.

refs JS-1678

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@linear-code
Copy link

linear-code bot commented Mar 19, 2026

@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2026

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


New Features ✨

Deps

  • Bump mongodb-memory-server-global from 10.1.4 to 11.0.1 by dependabot in #19888
  • Bump stacktrace-parser from 0.1.10 to 0.1.11 by dependabot in #19887

Other

  • (browser) Replace element timing spans with metrics by logaretm in #19869

Bug Fixes 🐛

Core

  • Do not overwrite user provided conversation id in Vercel by nicohrubec in #19903
  • Return same value from startSpan as callback returns by s1gr1d in #19300

Deps

  • Bump next to 15.5.14 in nextjs-15 and nextjs-15-intl E2E test apps by chargome in #19917
  • Bump socket.io-parser to 4.2.6 to fix CVE-2026-33151 by chargome in #19880

Other

  • (cloudflare) Forward ctx argument to Workflow.do user callback by Lms24 in #19891
  • (craft) Add missing mainDocsUrl for @sentry/effect SDK by bc-sentry in #19860
  • (nestjs) Add node to nest metadata by chargome in #19875
  • (serverless) Add node to metadata by nicohrubec in #19878

Internal Changes 🔧

Deps Dev

  • Bump qunit-dom from 3.2.1 to 3.5.0 by dependabot in #19546
  • Bump @react-router/node from 7.13.0 to 7.13.1 by dependabot in #19544

Other

  • (astro) Re-enable server island tracing e2e test in Astro 6 by Lms24 in #19872
  • (ci) Fix "Gatbsy" typo in issue package label workflow by chargome in #19905
  • (lint) Resolve oxlint warnings by isaacs in #19893
  • (node-integration-tests) Remove unnecessary file-type dependency by Lms24 in #19824
  • (sveltekit) Replace recast + @babel/parser with acorn by roli-lpci in #19533
  • Add external contributor to CHANGELOG.md by javascript-sdk-gitflow in #19909

🤖 This preview updates automatically when you update the PR.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2026

size-limit report 📦

⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Path Size % Change Change
@sentry/browser 25.69 kB +0.2% +49 B 🔺
@sentry/browser - with treeshaking flags 24.17 kB +0.14% +33 B 🔺
@sentry/browser (incl. Tracing) 42.11 kB -1.2% -511 B 🔽
@sentry/browser (incl. Tracing, Profiling) 46.72 kB -1.19% -559 B 🔽
@sentry/browser (incl. Tracing, Replay) 80.91 kB -0.63% -512 B 🔽
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 70.53 kB -0.66% -468 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 85.62 kB -0.59% -500 B 🔽
@sentry/browser (incl. Tracing, Replay, Feedback) 97.9 kB -0.49% -473 B 🔽
@sentry/browser (incl. Feedback) 42.48 kB +0.08% +30 B 🔺
@sentry/browser (incl. sendFeedback) 30.35 kB +0.15% +43 B 🔺
@sentry/browser (incl. FeedbackAsync) 35.4 kB +0.12% +39 B 🔺
@sentry/browser (incl. Metrics) 26.96 kB +0.15% +38 B 🔺
@sentry/browser (incl. Logs) 27.1 kB +0.12% +32 B 🔺
@sentry/browser (incl. Metrics & Logs) 27.78 kB +0.15% +39 B 🔺
@sentry/react 27.45 kB +0.22% +58 B 🔺
@sentry/react (incl. Tracing) 44.44 kB -1.15% -513 B 🔽
@sentry/vue 30.13 kB +0.16% +46 B 🔺
@sentry/vue (incl. Tracing) 44.02 kB -1.05% -467 B 🔽
@sentry/svelte 25.7 kB +0.16% +40 B 🔺
CDN Bundle 28.35 kB +0.27% +75 B 🔺
CDN Bundle (incl. Tracing) 43.22 kB -0.67% -289 B 🔽
CDN Bundle (incl. Logs, Metrics) 29.22 kB +0.27% +77 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) 44.07 kB -0.66% -289 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) 68.29 kB +0.13% +85 B 🔺
CDN Bundle (incl. Tracing, Replay) 80.06 kB -0.35% -275 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 80.98 kB -0.31% -251 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) 85.62 kB -0.3% -250 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 86.5 kB -0.31% -268 B 🔽
CDN Bundle - uncompressed 82.7 kB +0.1% +77 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 128.09 kB -0.37% -472 B 🔽
CDN Bundle (incl. Logs, Metrics) - uncompressed 85.57 kB +0.1% +77 B 🔺
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 130.96 kB -0.36% -472 B 🔽
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 209.22 kB +0.05% +102 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 244.97 kB -0.19% -447 B 🔽
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 247.82 kB -0.19% -447 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 257.88 kB -0.18% -447 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 260.72 kB -0.18% -447 B 🔽
@sentry/nextjs (client) 46.86 kB -1.08% -510 B 🔽
@sentry/sveltekit (client) 42.59 kB -1.13% -486 B 🔽
@sentry/node-core 56.42 kB +0.14% +74 B 🔺
@sentry/node 173.38 kB +0.13% +221 B 🔺
@sentry/node - without tracing 96.43 kB +0.1% +87 B 🔺
@sentry/aws-serverless 113.44 kB +0.09% +100 B 🔺

View base workflow run

logaretm and others added 2 commits March 19, 2026 10:42
Rewrite Playwright integration tests to expect Sentry distribution
metrics instead of spans, matching the new elementTimingIntegration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Contributor

github-actions bot commented Mar 19, 2026

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.
⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 9,143 - 9,110 +0%
GET With Sentry 1,667 18% 1,687 -1%
GET With Sentry (error only) 6,186 68% 6,012 +3%
POST Baseline 1,207 - 1,182 +2%
POST With Sentry 588 49% 589 -0%
POST With Sentry (error only) 1,069 89% 1,038 +3%
MYSQL Baseline 3,209 - 3,260 -2%
MYSQL With Sentry 480 15% 446 +8%
MYSQL With Sentry (error only) 2,678 83% 2,646 +1%

View base workflow run

logaretm and others added 2 commits March 19, 2026 13:05
Metrics are buffered and flushed after 5 seconds. The previous test
used a 3 second timeout which wasn't enough. Now properly waits for
metric envelopes with adequate timeouts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The integration was only exported from the npm package entry point
but not from the CDN bundle entry points, causing Sentry.elementTimingIntegration
to be undefined in bundle_tracing_* test configurations.

Verified locally:
- PW_BUNDLE=bundle_tracing_logs_metrics: 2 passed
- PW_BUNDLE=bundle_tracing_replay_feedback_logs_metrics_min: 2 passed
- browser-utils unit tests: 132 passed
- browser unit tests: 545 passed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@logaretm logaretm marked this pull request as ready for review March 19, 2026 18:06
@logaretm logaretm requested review from Lms24 and Copilot March 19, 2026 18:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR migrates Element Timing collection in the browser SDK from (awkward) span modeling to Sentry distribution metrics, by removing Element Timing span creation from browserTracingIntegration and introducing a standalone elementTimingIntegration export.

Changes:

  • Remove Element Timing span tracking from browserTracingIntegration and deprecate the enableElementTiming option.
  • Add elementTimingIntegration (re-exported from @sentry-internal/browser-utils) and emit element_timing.render_time / element_timing.load_time as distribution metrics with element.identifier and element.paint_type attributes.
  • Update unit + Playwright integration tests to validate metric emission instead of spans.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
packages/browser/src/tracing/browserTracingIntegration.ts Stops starting Element Timing tracking; deprecates enableElementTiming.
packages/browser/src/index.ts Re-exports elementTimingIntegration for SDK consumers.
packages/browser/src/index.bundle.tracing.ts Re-exports elementTimingIntegration in tracing bundle.
packages/browser/src/index.bundle.tracing.replay.ts Re-exports elementTimingIntegration in tracing+replay bundle.
packages/browser/src/index.bundle.tracing.replay.logs.metrics.ts Re-exports elementTimingIntegration in tracing+replay+logs+metrics bundle.
packages/browser/src/index.bundle.tracing.replay.feedback.ts Re-exports elementTimingIntegration in tracing+replay+feedback bundle.
packages/browser/src/index.bundle.tracing.replay.feedback.logs.metrics.ts Re-exports elementTimingIntegration in tracing+replay+feedback+logs+metrics bundle.
packages/browser/src/index.bundle.tracing.logs.metrics.ts Re-exports elementTimingIntegration in tracing+logs+metrics bundle.
packages/browser-utils/src/metrics/elementTiming.ts Implements elementTimingIntegration emitting distribution metrics; makes startTrackingElementTiming a deprecated no-op.
packages/browser-utils/src/index.ts Exports elementTimingIntegration from browser-utils package surface.
packages/browser-utils/test/metrics/elementTiming.test.ts Reworks unit tests to assert metrics.distribution calls instead of spans.
dev-packages/browser-integration-tests/suites/tracing/metrics/element-timing/test.ts Updates Playwright tests to assert metric envelopes and identifiers.
dev-packages/browser-integration-tests/suites/tracing/metrics/element-timing/init.js Enables Sentry.elementTimingIntegration() in the test app init.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +34 to +35
} catch {
return [];
…ing tests

Instead of sleeping 8 seconds and hoping all metrics have arrived,
poll until the expected element identifiers appear in the collected
metrics. This makes the tests faster (6-13s vs 16-20s) and more
reliable — they complete as soon as data arrives and fail with clear
assertions when it doesn't.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Dead default value for removed enableElementTiming usage
    • Removed the dead enableElementTiming: true default value from DEFAULT_BROWSER_TRACING_OPTIONS since the option is deprecated and no longer used.

Create PR

Or push these changes by commenting:

@cursor push 8056bbebb4
Preview (8056bbebb4)
diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/element-timing/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/element-timing/test.ts
--- a/dev-packages/browser-integration-tests/suites/tracing/metrics/element-timing/test.ts
+++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/element-timing/test.ts
@@ -96,14 +96,7 @@
     await page.goto(url);
 
     // Wait until all expected element identifiers have been flushed as metrics
-    await collector.waitForIdentifiers([
-      'image-fast',
-      'text1',
-      'button1',
-      'image-slow',
-      'lazy-image',
-      'lazy-text',
-    ]);
+    await collector.waitForIdentifiers(['image-fast', 'text1', 'button1', 'image-slow', 'lazy-image', 'lazy-text']);
 
     const allMetrics = collector.getAll().filter(m => m.name.startsWith('element_timing.'));
     const renderTimeMetrics = allMetrics.filter(m => m.name === 'element_timing.render_time');

diff --git a/packages/browser/src/tracing/browserTracingIntegration.ts b/packages/browser/src/tracing/browserTracingIntegration.ts
--- a/packages/browser/src/tracing/browserTracingIntegration.ts
+++ b/packages/browser/src/tracing/browserTracingIntegration.ts
@@ -334,7 +334,6 @@
   enableLongTask: true,
   enableLongAnimationFrame: true,
   enableInp: true,
-  enableElementTiming: true,
   ignoreResourceSpans: [],
   ignorePerformanceApiSpans: [],
   detectRedirects: true,

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.


const {
enableInp,
enableElementTiming,
Copy link

Choose a reason for hiding this comment

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

Deprecated option silently ignored without runtime warning

Medium Severity

The enableElementTiming option previously defaulted to true, meaning all browserTracingIntegration users automatically received element timing data. Now the option is deprecated and silently ignored — no runtime warning is emitted in DEBUG_BUILD. Users who relied on this default behavior will silently lose element timing data with no indication that they need to add elementTimingIntegration() to their integrations array. This is effectively a breaking change to a public API option without a runtime deprecation notice to guide migration.

Additional Locations (1)
Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

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 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

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

});
},
};
}) satisfies IntegrationFn;
Copy link

Choose a reason for hiding this comment

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

Missing integration/E2E test for new feat PR

Low Severity

The PR updates an existing integration test from span-based assertions to metric-based assertions but significantly weakens the test coverage. The old tests thoroughly validated metric values (render time ranges, load time ranges, relative ordering), element metadata (tag type, size, URL, id), and span structure. The new tests only check that identifiers appear in the correct metric names and validate paint type for two elements. Metric values, element metadata, and boundary checks are not asserted, reducing confidence that the integration works correctly end-to-end.

Fix in Cursor Fix in Web

Triggered by project rule: PR Review Guidelines for Cursor Bot

The property was removed from defaults but the type still required it,
causing a build error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

ElementTiming span timestamp uses render time

2 participants