feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat(browser): Add option to explicitly end pageload span via reportPageLoaded() - #17697

Merged
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii
Sep 19, 2025
Merged

feat(browser): Add option to explicitly end pageload span via reportPageLoaded()#17697
Lms24 merged 7 commits into
developfrom
lms/feat-browser-reportPageLoaded-ii

Conversation

@Lms24

@Lms24Lms24 commented Sep 18, 2025

Copy link
Copy Markdown
Member

This PR adds new functionality to browserTracingIntegration: By setting the new enableReportPageLoaded option to true, users can take full control* of the pageload span duration. It will stay active until Sentry.reportPageLoaded() is called*. This new functionality is opt-in and nothing changes for the default idle span mechanism.

* there are a couple of caveats and implications to this behaviour:

  • the pageload span is still started as an idle span and finalTimeout still acts as a safeguard to end the pageload span if the final timeout (30s by default) is reached. Just like by default, the span will be ended after the final timeout and the ending reason is set to 'finalTimeout'.
  • by default, the pageload span's end time stamp is trimmed to the end time of the longest running child span. This no longer applies when users set enableReportPageLoaded: true. In this case, the pageload span's end time stamp or duration will not be adjusted.
  • if a new navigation span is started, the pageload span is ended with ending reason 'cancelled'. My reasoning for this behaviour is that the entire SDK behaviour relies on the navigation span being another root span (with the exception of redirects). Ending the pageload span manually is already quite hard, so I'd argue we leave it at this for now and revisit potentially on request.

Usage

Sentry.init({integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true})],tracesSampleRate: 1,// ...});// whenever you define the end of your pageload, callSentry.reportPageLoaded();

Reviewers, happy to chat about the decisions and also happy to hear better suggestions!

Out of scope: Navigation spans

With this PR we don't provide users an API to control navigation span ending in the same way and I'd suggest we leave it this way for the time being. The reason is that a pageload is started once (at SDK init time) automatically and therefore, users only need to control the end time of this one span. Which IMHO is already hard enough to get right.

If we throw navigation spans into the mix we're in a bit of a weird state where the SDK takes care of automatically starting spans repeatedly, but hands down the responsibility to end them to users. I would argue that for users interested in taking control over the navigation span, too, the more fitting use case is to also start them like so:

Sentry.init({dsn: 'https://public@dsn.ingest.sentry.io/1337',integrations: [Sentry.browserTracingIntegration({enableReportPageLoaded: true,instrumentNavigation: false})],tracesSampleRate: 1,debug: true,});// Whenever you consider the page loadedSentry.reportPageLoaded();constnavigationSpan=Sentry.startBrowserTracingNavigationSpan(Sentry.getClient(),{name: 'custom_navigation'});// whenever you're ready:navigationSpan.end();

The missing piece here is an option to control idling behavior but we can adjust this on demand and perhaps pass down the newly introduced trimIdleSpanEndTimestamp to startBrowserTracingNavigationSpan.

closes#14810

cursor[bot]

This comment was marked as outdated.

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

size-limit report 📦

PathSize% ChangeChange
@sentry/browser24.23 kB--
@sentry/browser - with treeshaking flags22.74 kB--
@sentry/browser (incl. Tracing)40.34 kB+0.23%+89 B 🔺
@sentry/browser (incl. Tracing, Replay)78.72 kB+0.13%+100 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags68.38 kB+0.15%+100 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas)83.39 kB+0.13%+104 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback)95.59 kB+0.1%+94 B 🔺
@sentry/browser (incl. Feedback)40.95 kB--
@sentry/browser (incl. sendFeedback)28.88 kB--
@sentry/browser (incl. FeedbackAsync)33.8 kB--
@sentry/react25.94 kB--
@sentry/react (incl. Tracing)42.32 kB+0.19%+78 B 🔺
@sentry/vue28.72 kB--
@sentry/vue (incl. Tracing)42.14 kB+0.21%+87 B 🔺
@sentry/svelte24.25 kB--
CDN Bundle25.74 kB--
CDN Bundle (incl. Tracing)40.16 kB+0.28%+109 B 🔺
CDN Bundle (incl. Tracing, Replay)76.38 kB+0.15%+111 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback)81.88 kB+0.14%+107 B 🔺
CDN Bundle - uncompressed75.23 kB--
CDN Bundle (incl. Tracing) - uncompressed118.89 kB+0.26%+297 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed234.01 kB+0.13%+297 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed246.78 kB+0.13%+297 B 🔺
@sentry/nextjs (client)44.34 kB+0.23%+99 B 🔺
@sentry/sveltekit (client)40.76 kB+0.21%+85 B 🔺
@sentry/node-core49.96 kB--
@sentry/node152.17 kB--
@sentry/node - without tracing91.86 kB+0.01%+1 B 🔺
@sentry/aws-serverless105.31 kB--

View base workflow run

@github-actions

github-actionsBot commented Sep 18, 2025

Copy link
Copy Markdown
Contributor

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.

ScenarioRequests/s% of BaselinePrev. Requests/sChange %
GET Baseline8,913-8,789+1%
GET With Sentry1,32515%1,325-
GET With Sentry (error only)5,84866%6,033-3%
POST Baseline1,159-1,158+0%
POST With Sentry49543%492+1%
POST With Sentry (error only)1,03389%969+7%
MYSQL Baseline3,253-3,204+2%
MYSQL With Sentry40612%409-1%
MYSQL With Sentry (error only)2,63481%2,618+1%

View base workflow run

Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated
* A hook for the browser tracing integrations to trigger the end of a page load span.
* @returns {() => void} A function that, when executed, removes the registered callback.
*/
public on(hook: 'endPageloadSpan', callback: () => void): () => void;

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

I decided to go with the client hooks approach instead of exposing a method on browserTracingIntegration because in contrast to all other integrations, we don't hide the specific implementation of browserTracingIntegration behind defineIntegration. So adding a method would indeed be public API (we also can't introduce defineIntegration now as that would be a breaking change).

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.

IMHO this is a nice enough API for this anyhow, I like it!

Comment threadpackages/core/src/tracing/idleSpan.ts
Comment threadpackages/core/src/tracing/idleSpan.ts
@Lms24
Lms24 requested review from mydea and s1gr1dSeptember 18, 2025 15:48
@Lms24Lms24 self-assigned this Sep 18, 2025
@Lms24
Lms24force-pushed the lms/feat-browser-reportPageLoaded-ii branch from 3623454 to c1762f1CompareSeptember 19, 2025 07:23
Comment threadpackages/browser/src/tracing/browserTracingIntegration.ts Outdated

@mydeamydea 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.

name nit maybe, but also no strong opinion either way - overall this is really nice, love to finally see this!

@s1gr1ds1gr1d 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 like this :)

Was thinking about renaming the option to enableManualPageLoadEnd or something different including the word manual but enableReportPageLoaded is actually nice because it resembles the actual API you need to call.

@Lms24
Lms24 enabled auto-merge (squash) September 19, 2025 12:53
@Lms24
Lms24 merged commit c123105 into developSep 19, 2025
367 of 370 checks passed
@Lms24
Lms24 deleted the lms/feat-browser-reportPageLoaded-ii branch September 19, 2025 15:33
@alt1o

Copy link
Copy Markdown

That's great!! I'm just looking for this feature. Wonder when this will release.

@s1gr1d

Copy link
Copy Markdown
Member

It was released in 10.13.0 :)

Sign up for freeto 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.

Programatic way to indicate that a pageload / navigation transaction has completed

4 participants

@Lms24@alt1o@s1gr1d@mydea