Skip to content

Conversation

@s1gr1d
Copy link
Member

@s1gr1d s1gr1d commented Nov 12, 2025

Adds the manual mode for profiling and browser integration tests.

  • adds deprecation note for old option
  • adds some JSDoc comments to public-facing API to make the difference between Node and UI profiling better visible.

Closes #17279

@github-actions
Copy link
Contributor

github-actions bot commented Nov 12, 2025

size-limit report 📦

Path Size % Change Change
@sentry/browser 24.62 kB - -
@sentry/browser - with treeshaking flags 23.13 kB - -
@sentry/browser (incl. Tracing) 41.28 kB - -
@sentry/browser (incl. Tracing, Profiling) 45.86 kB +0.55% +250 B 🔺
@sentry/browser (incl. Tracing, Replay) 79.73 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 69.44 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 84.43 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 96.64 kB - -
@sentry/browser (incl. Feedback) 41.29 kB - -
@sentry/browser (incl. sendFeedback) 29.29 kB - -
@sentry/browser (incl. FeedbackAsync) 34.21 kB - -
@sentry/react 26.32 kB - -
@sentry/react (incl. Tracing) 43.22 kB - -
@sentry/vue 29.11 kB - -
@sentry/vue (incl. Tracing) 43.08 kB - -
@sentry/svelte 24.64 kB - -
CDN Bundle 27.03 kB +0.33% +88 B 🔺
CDN Bundle (incl. Tracing) 41.93 kB +0.26% +105 B 🔺
CDN Bundle (incl. Tracing, Replay) 78.48 kB +0.14% +108 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 83.95 kB +0.12% +98 B 🔺
CDN Bundle - uncompressed 79.3 kB +0.48% +378 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 124.45 kB +0.31% +378 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 240.48 kB +0.16% +378 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 253.25 kB +0.15% +378 B 🔺
@sentry/nextjs (client) 45.37 kB - -
@sentry/sveltekit (client) 41.67 kB - -
@sentry/node-core 50.91 kB +0.01% +1 B 🔺
@sentry/node 159.09 kB - -
@sentry/node - without tracing 92.78 kB +0.01% +1 B 🔺
@sentry/aws-serverless 106.53 kB +0.01% +1 B 🔺

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Nov 14, 2025

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.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 8,901 - 9,158 -3%
GET With Sentry 1,405 16% 1,276 +10%
GET With Sentry (error only) 6,288 71% 5,987 +5%
POST Baseline 1,224 - 1,166 +5%
POST With Sentry 561 46% 502 +12%
POST With Sentry (error only) 1,090 89% 1,041 +5%
MYSQL Baseline 3,412 - 3,304 +3%
MYSQL With Sentry 523 15% 425 +23%
MYSQL With Sentry (error only) 2,775 81% 2,662 +4%

View base workflow run

this._sessionSampled = sessionSampled;
this._lifecycleMode = lifecycleMode;

client.on('spanStart', span => {
Copy link
Member Author

Choose a reason for hiding this comment

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

The client.on calls (spanStart, spanEnd) are now added in the _setupTraceLifecycleListeners function.

s1gr1d added a commit that referenced this pull request Nov 17, 2025
This PR was factored out of another PR to make reviewing easier. The
other PR: #18189

Moved the `spanStart` and `spanEnd` listeners into an extra function
(`_setupTraceLifecycleListeners`) to be able to only call it depending
on the lifecycle (used in another PR).

Part of #17279
# Conflicts:
#	packages/browser/src/profiling/UIProfiler.ts
@s1gr1d s1gr1d marked this pull request as ready for review November 17, 2025 13:29
Copy link
Member Author

Choose a reason for hiding this comment

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

This file is similar to the Node profiling one: packages/core/src/profiling.ts (can be seen at the end of this diff page)

// Trace: Profile context is kept as long as there is an active root span
if (this._lifecycleMode === 'manual') {
getGlobalScope().setContext('profile', {});
}
Copy link

Choose a reason for hiding this comment

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

Bug: Profiling state leaks after trace completion.

In trace mode, when _endProfiling is called after the last root span ends, the profile context is not cleared. The condition if (this._lifecycleMode === 'manual') prevents clearing the context in trace mode, but when there are no active root spans (which is when _endProfiling is called from spanEnd), the context should be cleared to prevent subsequent transactions from being incorrectly marked as profiled. The profile context persists even though profiling has stopped, causing spans created after profiling ends to incorrectly appear as profiled.

Fix in Cursor Fix in Web

Copy link
Member Author

Choose a reason for hiding this comment

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

The profiler context will stay the same for the whole profiling session as the profiler cannot be just stopped like in manual profiling, where there might be spans that are not profiled anymore.

Copy link
Member

@Lms24 Lms24 left a comment

Choose a reason for hiding this comment

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

Nice work! Had one thought about the public API <> integration interaction but otherwise LGTM!

Comment on lines +36 to +37
// In manual mode we start and stop once -> expect exactly one chunk
const profileChunkEnvelopes = await getMultipleSentryEnvelopeRequests<ProfileChunkEnvelope>(
Copy link
Member

Choose a reason for hiding this comment

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

l: the code suggests we get two envelopes/chunks. Should we update/remove the comment?

const _browserProfilingIntegration = (() => {
return {
name: INTEGRATION_NAME,
_profiler: new UIProfiler(),
Copy link
Member

Choose a reason for hiding this comment

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

l/m: In case the CDN bundle tests fail, this is likely the reason why: _profiler is mangled by our terser config (makeTerserPlugin()) because it is not excluded from the ignore list of private properties.

We can either opt out of mangling this field, make it "public" (i.e. remove the leading underscore), or add client hooks instead of exposing any additional field. I'd personally prefer using client hooks because it makes the binding very loose and also removes the need for the isProfilingIntegrationWithProfiler type predicate. Happy to leave this up to you though!

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.

[Browser]: Implement new Profiling API spec (UI Profiling)

3 participants