Repository navigation
Conversation
|
bugbot run |
size-limit report 📦
|
217546f to
fa2f4e1
Compare
|
bugbot run |
fa2f4e1 to
c617c75
Compare
|
bugbot run |
c617c75 to
ecb46df
Compare
|
bugbot run |
ecb46df to
8ead4b1
Compare
|
bugbot run |
|
bugbot run |
d3fd241 to
1ae9f0e
Compare
|
bugbot run |
|
bugbot run |
7c3abfc to
2358cdb
Compare
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 2358cdb. Configure here.
2358cdb to
85c7756
Compare
669a79a to
e0b0cae
Compare
9f0f16b to
c248cc5
Compare
| * How long the pageload start may wait for the resumed part of a Cache Components document. The wait | ||
| * normally ends with `DOMContentLoaded`; the cap only matters for a response the server keeps open. | ||
| */ | ||
| const PAGELOAD_TRACE_META_TAG_TIMEOUT_MS = 10_000; |
There was a problem hiding this comment.
this one is arbitrarily chosen and should likely never be met in real world conditions
| if (instrumentPageLoad) { | ||
| client.on('startNavigationSpan', settlePendingPageloadWait); | ||
| } |
There was a problem hiding this comment.
M: this hook is registered in afterAllSetup which runs later than setup (this is where the other browserTracingInstrumentation calls this hook).
Can we add an E2E test that clicks a link during a slow dynamic hole? I think that would catch it.
Because if we now have a navigation that starts during the waiting time (slow dynamic hole), we would get a navigation span before the pageload span. maybeEndActiveSpan() would probably cancel that. Definitely worth testing at least.
| const scope = getCurrentScope(); | ||
| const { normalizedRequest } = scope.getScopeData().sdkProcessingMetadata; | ||
| if (normalizedRequest) { | ||
| scope.setSDKProcessingMetadata({ normalizedRequest: { ...normalizedRequest, url: documentUrl } }); |
There was a problem hiding this comment.
What do we get in the documentUrl? Does this need a check on dataCollection.urlQueryParams?
There was a problem hiding this comment.
I'll check again and guard it if needed
| span.addLink({ | ||
| context: { | ||
| traceId: traceparent.traceId, | ||
| spanId: traceparent.parentSpanId, | ||
| traceFlags: traceparent.parentSampled ? TRACE_FLAG_SAMPLED : TRACE_FLAG_NONE, | ||
| }, | ||
| }); |
There was a problem hiding this comment.
We should probably add a new link type for that. Maybe server_request?
There was a problem hiding this comment.
Adding the link is a very rare case do you think this is actually needed? What would we gain?
There was a problem hiding this comment.
The information in the Sentry frontend what this link actually is. Right now, it's just linking but without contextual information about the meaning of this link.
|
👋 @nicohrubec — Please review this PR when you get a chance! |
… the pageload A document that resumes a prerendered shell gets its sentry-trace tag with the resumed chunk, which lands in <body> after the SDK has usually already run. A pageload created at that point can only link the server trace. Continue whatever tag is in the document, last one wins, and when there is no tag, no Server-Timing trace and the document is still streaming, wait for the tag before creating the pageload span, bounded by DOMContentLoaded and a 10 second cap. The span's start is backdated to the time origin and its children come from the performance timeline, so it looks the same as before, only inside the server's trace. A client-side navigation during the wait starts the pageload with a fresh trace first, so the navigation is filed as its redirect instead of being the only record of the visit. Fixes #24944 Refs JS-3863 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…a navigation A pageload that waits for its trace meta tag can be settled by a navigation, and a `popstate` has already changed the location by then. The browser SDK reads the location when the span starts, so the pageload got the URL of the navigation. Capture the document URL when the instrumentation sets up and put it on the span and the request data instead. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ntions constants in the test Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The Next.js routing instrumentation never flags a navigation as a redirect, so a navigation during the wait ends the pageload like any pageload it interrupts and starts its own trace, as it does today. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s on The wait exists for a document that resumes a prerendered shell, whose tag arrives with the resumed part. Without `cacheComponents` a tag is either in `<head>` from the start or never comes, so waiting only held the pageload back: fetches before the tag were not attached and the span ended earlier. The build now injects `_sentryCacheComponents`, and the client only waits when it is set. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Under `cacheComponents` the SDK did not enable `experimental.clientTraceMetadata` (#21141). A shell that Next.js prerenders during one request is cached and served to later visitors, and a `sentry-trace` tag inside it would attach all of them to that one request. ISR pages had the same problem, worked around on the client by removing the tags on routes with `generateStaticParams` (#18192). Next.js computes the trace tags of a document inside the `prerender route (app)` span when it prerenders, and outside of it when it renders for a request, including when it resumes a shell. The server SDK now marks that span on the OpenTelemetry context, and its propagator hands out nothing on a marked context. A prerendered shell never contains a trace, so any tag that reaches the browser belongs to the current request. The mark sits on the context rather than the span because an unsampled request makes the span non-recording, with no attributes to recognize it by, and a `sampled=0` tag in the shell would drop every later pageload. Checked in Next.js 15.0 through 16.4. With that, `clientTraceMetadata` is enabled again under `cacheComponents`, and the client-side ISR workaround and the `isrRoutes` manifest field are removed. The guard covers ISR shells too, including `revalidate` routes the file scan never found. The guard is applied to the tracer provider and propagator the Node SDK registers, and `withSentry` of `@sentry/nextjs/cloudflare` applies it to its own setup, since ISR pages revalidate inside a request of the Worker. A custom OpenTelemetry setup does not get it automatically. If it registers `SentryPropagator`, it can register the exported `NextSentryPropagator` instead and pass its tracer provider to `markPrerendersOnTracerProvider`. Setups with only the OpenTelemetry W3C propagators, which includes `@vercel/otel` by default, write no `sentry-trace` tag, so nothing changes for them. The docs follow in sentry-docs. An ISR page with `revalidate = 1` makes the e2e suite exercise the guard at runtime; the other ISR shells come from the build, where the SDK does not run. On its own this PR keeps the current client behaviour: tags in `<head>` are continued and are now always fresh. The tag of a resumed shell arrives with the resumed part, which the client only reads in #25142. <img width="2240" height="1332" alt="figure-shell" src="https://github.com/user-attachments/assets/809726dd-cddb-4fab-bfee-83bcdc35dbab" /> Refs #24944, see #24298 --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: s1gr1d <32902192+s1gr1d@users.noreply.github.com>
c248cc5 to
7a4cf16
Compare
`browserTracingIntegration` registers its `startNavigationSpan` handler in `setup`, before the Next.js handler in `afterAllSetup`. A navigation during the wait therefore created its span first, and the late pageload ended it. Settling on `beforeStartNavigationSpan` does not depend on that order.
…uest data The pageload wrote the raw document URL into `normalizedRequest`, so the `dataCollection.urlQueryParams` denylist no longer applied to `request.url` of any App Router pageload. The URL is now filtered once, when it is captured.
Without a type, the link from a pageload to the server request whose trace context arrived too late shows up in Sentry with no meaning. The type is documented in getsentry/sentry-docs#20112.
Builds on #25141.
When a document resumes a prerendered shell, its
sentry-tracetag sits at the start of the resumed part, which arrives after the SDK has started. In a local demo six of seven visits found no tag at init, so the pageload could only link the server trace instead of joining it.The app router pageload now continues whatever tag is in the document, last one wins. In apps with
cacheComponentsenabled, if there is no tag, noServer-Timingtrace and the document is still streaming, it waits for the tag before creating the pageload span. Other apps keep the immediate start: withoutcacheComponentsa tag is either in<head>from the start or never comes, so waiting only held the pageload back. The wait ends atDOMContentLoadedor after 10 seconds. The span still starts at the time origin and gets its children from the performance timeline, so it looks the same as before, only inside the server's trace. No tag by the end of parsing means a fresh trace, which is the fully static case.A client-side navigation during the wait starts the pageload with a fresh trace first, so the navigation ends it like any pageload it interrupts, as today. Started later, the pageload would end the navigation span instead. The pageload keeps the URL the document was loaded with, even if the navigation already changed it.
Waiting was chosen over moving a started span to another trace. That needs core changes and does not work with span streaming once children are flushed. The cost: fetches, errors and clicks between SDK start and the resumed part are not attached to the pageload. The
Server-Timingfollow-ups (JS-3864, JS-3866) remove that window where the header can be set.Fixes #24944
🤖 Generated with Claude Code