Skip to content

Conversation

Lms24
Copy link
Member

@Lms24 Lms24 commented Sep 8, 2025

UPDATE: This PR was reworked quite extensively in e8fa689. PR description was updated accordingly. Would appreciate a fresh round of reviews!

This PR adds a few attributes to resource.* spans for request timing and status information. Most importantly:

To get these attributes, I adjusted the already existing resourceTimingToSpanAttributes a bit:

  • Moved it from browser to browser-utils because resource spans logic is located in browser-utils (this is safe, it never was exported from browser)
  • Changed the signature to return an object instead of an array of attributes. This is more ergonomical to use in both callsites and should reduce bundle size slightly
  • Added http.request.redirect_end, http.request.worker_start attributes which are stable timing values available on PerformanceResourceTiming but were previously missing
  • Added http.request.time_to_first_byte. Decided to add this attribute because http.request.response_start cannot be directly used as TTFB as its value is an absolute time stamp of responseStart. Instead the TTFB attribute is the relative response start attribute converted to seconds (happy to change to ms if reviewers prefer).

Other consequences:

  • http.client spans now also have the three additional attributes

Remarks:

  • Not super happy about us always defaulting to 0 in case a value is not present. But decided to leave this as-is to avoid any behaviour change for http.client spans.

closes #16805
closes #10995
closes #17554
ref getsentry/sentry#63739

Copy link
Contributor

github-actions bot commented Sep 8, 2025

size-limit report 📦

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

Path Size % Change Change
@sentry/browser 24.17 kB - -
@sentry/browser - with treeshaking flags 22.75 kB - -
@sentry/browser (incl. Tracing) 40.17 kB +0.03% +11 B 🔺
@sentry/browser (incl. Tracing, Replay) 78.53 kB -0.01% -1 B 🔽
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 68.26 kB -0.03% -17 B 🔽
@sentry/browser (incl. Tracing, Replay with Canvas) 83.19 kB -0.03% -21 B 🔽
@sentry/browser (incl. Tracing, Replay, Feedback) 95.4 kB -0.04% -37 B 🔽
@sentry/browser (incl. Feedback) 40.9 kB - -
@sentry/browser (incl. sendFeedback) 28.82 kB - -
@sentry/browser (incl. FeedbackAsync) 33.76 kB - -
@sentry/react 25.91 kB - -
@sentry/react (incl. Tracing) 42.17 kB +0.05% +20 B 🔺
@sentry/vue 28.66 kB - -
@sentry/vue (incl. Tracing) 41.98 kB +0.04% +15 B 🔺
@sentry/svelte 24.2 kB - -
CDN Bundle 25.76 kB - -
CDN Bundle (incl. Tracing) 40.06 kB +0.06% +21 B 🔺
CDN Bundle (incl. Tracing, Replay) 76.3 kB -0.03% -22 B 🔽
CDN Bundle (incl. Tracing, Replay, Feedback) 81.79 kB +0.01% +7 B 🔺
CDN Bundle - uncompressed 75.28 kB - -
CDN Bundle (incl. Tracing) - uncompressed 118.57 kB +0.06% +64 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 233.67 kB +0.03% +64 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 246.43 kB +0.03% +64 B 🔺
@sentry/nextjs (client) 44.17 kB +0.05% +18 B 🔺
@sentry/sveltekit (client) 40.61 kB +0.04% +13 B 🔺
@sentry/node-core 49.85 kB - -
@sentry/node 151.13 kB - -
@sentry/node - without tracing 91.73 kB - -
@sentry/aws-serverless 105.19 kB - -

View base workflow run

Copy link
Member

@AbhiPrasad AbhiPrasad left a comment

Choose a reason for hiding this comment

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

Before this get merged in, let's make sure they are documented in https://github.com/getsentry/sentry-conventions (and adjust the conventions accordingly if there are differences).

A while ago I also did this write up for uptime spans: https://www.notion.so/sentry/Uptime-Span-Conventions-1eb8b10e4b5d80799c70cec9a8a06402#1eb8b10e4b5d80799c70cec9a8a06402

@Lms24
Copy link
Member Author

Lms24 commented Sep 8, 2025

Good points, thanks Abhi!

So IIUC the notion doc correctly, the SDK should do the calculations for the specific phases in the request life cycle ("durations") instead of sending the raw start/end timestamp values? And in addition, send ttfb instead of something like response.start?
I'm technically fine with this but the disadvantage is likely bundle size and the SDK having to be smarter than necessary. Not opposed to it, just wondering what direction we should go with this.

I'll add whatever attributes we settle on to the conventions repo!

@AbhiPrasad
Copy link
Member

@Lms24 sorry I should have made this more clear. I prefer the approach you have in this PR. Let's just send as much data as we can, minimizing transforms in the SDK.

We can easily materialize this into the attributes that match what we have for uptime in Relay or during query time in the product.

I just shared the notion doc as an FYI.

Copy link
Contributor

github-actions bot commented Sep 9, 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.
⚠️ Warning: Base artifact is not the latest one, because the latest workflow run is not done yet. This may lead to incorrect results. Try to re-run all tests to get up to date results.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 9,205 - 8,916 +3%
GET With Sentry 1,325 14% 1,261 +5%
GET With Sentry (error only) 5,993 65% 5,771 +4%
POST Baseline 1,181 - 1,176 +0%
POST With Sentry 467 40% 467 -
POST With Sentry (error only) 1,062 90% 1,065 -0%
MYSQL Baseline 3,348 - 3,305 +1%
MYSQL With Sentry 461 14% 431 +7%
MYSQL With Sentry (error only) 2,681 80% 2,718 -1%

View base workflow run

cursor[bot]

This comment was marked as outdated.

@Lms24 Lms24 force-pushed the lms/feat-browser-resource-span-attributes branch from 75a4073 to 5a11163 Compare September 9, 2025 10:57
cursor[bot]

This comment was marked as outdated.

@Lms24
Copy link
Member Author

Lms24 commented Sep 9, 2025

I update this PR to use resourceTimingToSpanAttributes which already added most of the timing attributes to http.client spans. We should stick with consistent logic, especially since both, resource.* and http.client spans take its timing data from PerformanceResourceTiming performance entries.

Copy link
Member Author

Choose a reason for hiding this comment

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

I just moved this and the file below because these were incorrectly nested previously

if (isPerformanceResourceTiming(entry) && entry.name.endsWith(url)) {
const spanAttributes = resourceTimingToSpanAttributes(entry);
spanAttributes.forEach(attributeArray => span.setAttribute(...attributeArray));
span.setAttributes(resourceTimingToSpanAttributes(entry));
Copy link
Member Author

Choose a reason for hiding this comment

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

resourceTimingToSpanAttributes now directly returns an array, so we can directly call spanSetAttributes here. Saves a few bytes.

Comment on lines +55 to +58
// For TTFB we actually want the relative time from timeOrigin to responseStart
// This way, TTFB always measures the "first page load" experience.
// see: https://web.dev/articles/ttfb#measure-resource-requests
'http.request.time_to_first_byte': (resourceTiming.responseStart ?? 0) / 1000,
Copy link
Member Author

Choose a reason for hiding this comment

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

Curious on reviewers' opinions on this one: I decided to convert this value to seconds to stick with us mostly sending seconds-based values. Happy to leave at ms if reviewers prefer.

Copy link
Member

Choose a reason for hiding this comment

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

In terms of keeping the standard of seconds I would keep seconds here. It's a decimal number, right?

Copy link
Member

@s1gr1d s1gr1d left a comment

Choose a reason for hiding this comment

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

Nice!

Comment on lines +55 to +58
// For TTFB we actually want the relative time from timeOrigin to responseStart
// This way, TTFB always measures the "first page load" experience.
// see: https://web.dev/articles/ttfb#measure-resource-requests
'http.request.time_to_first_byte': (resourceTiming.responseStart ?? 0) / 1000,
Copy link
Member

Choose a reason for hiding this comment

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

In terms of keeping the standard of seconds I would keep seconds here. It's a decimal number, right?

'http.request.worker_start': expect.any(Number),
'http.request.response_end': expect.any(Number),
'http.request.response_start': expect.any(Number),
'http.request.time_to_first_byte': expect.any(Number),
Copy link
Member

Choose a reason for hiding this comment

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

Kinda related to the other comment: Maybe we can check here that it's a decimal number.

Copy link
Member Author

@Lms24 Lms24 Sep 12, 2025

Choose a reason for hiding this comment

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

Hmm so theoretically, this could also be an integer, if TTFB happens to be exactly 1000ms for example. We could test against something like expect(Number.isIntegrer(attributes['http.request.time_to_first_byte'])).toBe(false) but this could introduce flakiness (albeit unlikely). WDYT about rather doing a range check instead? 0 < ttfb < 10?

@Lms24 Lms24 force-pushed the lms/feat-browser-resource-span-attributes branch from c3bad5e to 32b3717 Compare September 12, 2025 14:44
@Lms24 Lms24 merged commit 7c41f03 into develop Sep 12, 2025
275 of 276 checks passed
@Lms24 Lms24 deleted the lms/feat-browser-resource-span-attributes branch September 12, 2025 15:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants