From 2a1a2629048f36c7171de7757797ede1b77b75d4 Mon Sep 17 00:00:00 2001 From: Martin Sonnberger Date: Mon, 27 Jul 2026 15:42:39 +0200 Subject: [PATCH] feat(browser)!: Use `browser.paint` span op for paint entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `paint` is not a registered op; `browser.paint` is. Only affects tracing spans — Session Replay performance entries are unchanged. Ref: JS-3105 Co-Authored-By: Claude Opus 5 (1M context) --- .../suites/tracing/metrics/web-vitals-fp-fcp/test.ts | 4 ++-- packages/browser-utils/src/metrics/browserMetrics.ts | 3 ++- packages/browser-utils/test/browser/browserMetrics.test.ts | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts index 9351baf09a0e..cd63b699cc06 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/metrics/web-vitals-fp-fcp/test.ts @@ -18,7 +18,7 @@ sentryTest('should capture FP vital.', async ({ browserName, getLocalTestUrl, pa const fpSpan = eventData.spans?.filter(({ description }) => description === 'first-paint')[0]; expect(fpSpan).toBeDefined(); - expect(fpSpan?.op).toBe('paint'); + expect(fpSpan?.op).toBe('browser.paint'); expect(fpSpan?.parent_span_id).toBe(eventData.contexts?.trace?.span_id); }); @@ -36,6 +36,6 @@ sentryTest('should capture FCP vital.', async ({ getLocalTestUrl, page }) => { const fcpSpan = eventData.spans?.filter(({ description }) => description === 'first-contentful-paint')[0]; expect(fcpSpan).toBeDefined(); - expect(fcpSpan?.op).toBe('paint'); + expect(fcpSpan?.op).toBe('browser.paint'); expect(fcpSpan?.parent_span_id).toBe(eventData.contexts?.trace?.span_id); }); diff --git a/packages/browser-utils/src/metrics/browserMetrics.ts b/packages/browser-utils/src/metrics/browserMetrics.ts index 25b1a449f613..9887f9a724a7 100644 --- a/packages/browser-utils/src/metrics/browserMetrics.ts +++ b/packages/browser-utils/src/metrics/browserMetrics.ts @@ -29,6 +29,7 @@ import { getNavigationEntry } from './web-vitals/lib/getNavigationEntry'; import { getVisibilityWatcher } from './web-vitals/lib/getVisibilityWatcher'; import { DEBUG_BUILD } from '../debug-build'; import { URL_FULL } from '@sentry/conventions/attributes'; +import { BROWSER_BROWSER_PAINT_SPAN_OP } from '@sentry/conventions/op'; interface NavigatorNetworkInformation { readonly connection?: NetworkInformation; } @@ -561,7 +562,7 @@ export function _addMeasureSpans( if (measureStartTimestamp <= measureEndTimestamp) { startAndEndSpan(span, measureStartTimestamp, measureEndTimestamp, { name: entry.name, - op: entry.entryType, + op: entry.entryType === 'paint' ? BROWSER_BROWSER_PAINT_SPAN_OP : entry.entryType, attributes, }); } diff --git a/packages/browser-utils/test/browser/browserMetrics.test.ts b/packages/browser-utils/test/browser/browserMetrics.test.ts index 8acb93833c2a..9ca1f8695178 100644 --- a/packages/browser-utils/test/browser/browserMetrics.test.ts +++ b/packages/browser-utils/test/browser/browserMetrics.test.ts @@ -280,7 +280,7 @@ describe('_addMeasureSpans', () => { expect.objectContaining({ description: 'measure-pass', op: 'measure' }), expect.objectContaining({ description: 'mark-pass', op: 'mark' }), // name matches but type is not (mark|measure) => should not be ignored - expect.objectContaining({ description: 'mark-ignore', op: 'paint' }), + expect.objectContaining({ description: 'mark-ignore', op: 'browser.paint' }), ]), ); });