Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
60ecb6e
feat(node): Allow selective tracking of `pino` loggers
timfish Oct 14, 2025
abdce67
fix(node): `pino` child loggers
timfish Oct 14, 2025
ad39cb4
Fix logic
timfish Oct 15, 2025
f23891a
Merge branch 'develop' into timfish/fix/pino-child-loggers
timfish Oct 15, 2025
e00cf68
Merge branch 'develop' into timfish/fix/pino-child-loggers
timfish Oct 15, 2025
536df18
Merge remote-tracking branch 'upstream/master' into timfish/fix/pino-…
timfish Oct 15, 2025
32cab77
Merge branch 'timfish/fix/pino-child-loggers' of github.com:getsentry…
timfish Oct 15, 2025
4308b37
docs: Update supported Angular version in README
guillaume-moreau Oct 14, 2025
908e4ec
fix(browser): Ignore React 19.2+ component render measure entries (#1…
Lms24 Oct 15, 2025
318ce67
feat(solid): Add support for TanStack Router Solid (#17735)
thedanchez Oct 15, 2025
715f12f
chore(ci): Update Next.js canary testing (#17939)
chargome Oct 15, 2025
1c4c62b
chore: Bump size limit (#17941)
chargome Oct 15, 2025
eb5a5a8
chore: Add external contributor to CHANGELOG.md (#17940)
HazAT Oct 15, 2025
e53277e
fix(react): Add `POP` guard for long-running `pageload` spans (#17867)
onurtemizkan Oct 15, 2025
d03fc61
fix(tracemetrics): Send boolean for internal replay attribute (#17908)
chargome Oct 15, 2025
6abafe3
meta(changelog): Update changelog for 10.20.0
chargome Oct 15, 2025
1b8de2e
Merge branch 'timfish/fix/pino-child-loggers' of github.com:getsentry…
timfish Oct 15, 2025
8eb3787
Child tests
timfish Oct 15, 2025
fe0a46e
Merge branch 'develop' into timfish/fix/pino-child-loggers
timfish Oct 16, 2025
1bc5b4f
No breaking changes!
timfish Oct 16, 2025
b25e47d
remove duplicate test
timfish Oct 16, 2025
fd0242a
Merge branch 'develop' into timfish/fix/pino-child-loggers
timfish Oct 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix(browser): Ignore React 19.2+ component render measure entries (#1…
…7905)

With 19.2, React introduced [custom perfomance
tracks](https://react.dev/blog/2025/10/01/react-19-2#performance-tracks)
in chrome dev tools. This track is populated by collecting
`performance.measure` entries for every component (re-)render. Sounds
good in theory but in reality this causes a massive performance
degradation when using the Sentry SDK because we collect spans from
`PerformanceMeasure` entries. In our Sentry UI, this caused 10+ second
long blocks because we created thousands of spans from these render
entries.

This patch fixes this performance drop by inspecting the measure entries'
`detail` object which we can use to _fairly well_ distinguish React's
entries from users' entries. Not 100% bulletproof but I think good
enough.
  • Loading branch information
Lms24 authored and timfish committed Oct 15, 2025
commit 908e4eca3e3e0f9454417a1466ed1728c9adf611
23 changes: 23 additions & 0 deletions packages/browser-utils/src/metrics/browserMetrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,24 @@ export function addPerformanceEntries(span: Span, options: AddPerformanceEntries
_measurements = {};
}

/**
* React 19.2+ creates performance.measure entries for component renders.
* We can identify them by the `detail.devtools.track` property being set to 'Components ⚛'.
* see: https://react.dev/reference/dev-tools/react-performance-tracks
* see: https://github.com/facebook/react/blob/06fcc8f380c6a905c7bc18d94453f623cf8cbc81/packages/react-reconciler/src/ReactFiberPerformanceTrack.js#L454-L473
*/
function isReact19MeasureEntry(entry: PerformanceEntry | null): boolean | void {
if (entry?.entryType !== 'measure') {
return;
}
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return (entry as PerformanceMeasure).detail.devtools.track === 'Components ⚛';
} catch {
return;
}
}

/**
* Create measure related spans.
* Exported only for tests.
Expand All @@ -437,6 +455,10 @@ export function _addMeasureSpans(
timeOrigin: number,
ignorePerformanceApiSpans: AddPerformanceEntriesOptions['ignorePerformanceApiSpans'],
): void {
if (isReact19MeasureEntry(entry)) {
return;
}

if (
['mark', 'measure'].includes(entry.entryType) &&
stringMatchesSomePattern(entry.name, ignorePerformanceApiSpans)
Expand All @@ -445,6 +467,7 @@ export function _addMeasureSpans(
}

const navEntry = getNavigationEntry(false);

const requestTime = msToSec(navEntry ? navEntry.requestStart : 0);
// Because performance.measure accepts arbitrary timestamps it can produce
// spans that happen before the browser even makes a request for the page.
Expand Down
69 changes: 69 additions & 0 deletions packages/browser-utils/test/browser/browserMetrics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,75 @@ describe('_addMeasureSpans', () => {
]),
);
});

it('ignores React 19.2+ measure spans', () => {
const pageloadSpan = new SentrySpan({ op: 'pageload', name: '/', sampled: true });
const spans: Span[] = [];

getClient()?.on('spanEnd', span => {
spans.push(span);
});

const entries: PerformanceMeasure[] = [
{
entryType: 'measure',
name: '\u200bLayout',
duration: 0.3,
startTime: 12,
detail: {
devtools: {
track: 'Components ⚛',
},
},
toJSON: () => ({ foo: 'bar' }),
},
{
entryType: 'measure',
name: '\u200bButton',
duration: 0.1,
startTime: 13,
detail: {
devtools: {
track: 'Components ⚛',
},
},
toJSON: () => ({}),
},
{
entryType: 'measure',
name: 'Unmount',
duration: 0.1,
startTime: 14,
detail: {
devtools: {
track: 'Components ⚛',
},
},
toJSON: () => ({}),
},
{
entryType: 'measure',
name: 'my-measurement',
duration: 0,
startTime: 12,
detail: null,
toJSON: () => ({}),
},
];

const timeOrigin = 100;
const startTime = 23;
const duration = 356;

entries.forEach(e => {
_addMeasureSpans(pageloadSpan, e, startTime, duration, timeOrigin, []);
});

expect(spans).toHaveLength(1);
expect(spans.map(spanToJSON)).toEqual(
expect.arrayContaining([expect.objectContaining({ description: 'my-measurement', op: 'measure' })]),
);
});
});

describe('_addResourceSpans', () => {
Expand Down