Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trace pageloads on Next.js app router #9571

Closed
Tracked by #9619
lforst opened this issue Nov 15, 2023 · 5 comments
Closed
Tracked by #9619

Trace pageloads on Next.js app router #9571

lforst opened this issue Nov 15, 2023 · 5 comments
Assignees
Labels
Package: nextjs Issues related to the Sentry Nextjs SDK

Comments

@lforst
Copy link
Member

lforst commented Nov 15, 2023

We would like to trace page loads for the Next.js app router. This would involve somehow passing trace data for Next.js app router SSR to the client for the initial page load.

Currently, we are blocked here by React/Next.js because there is no real way to transparently transfer trace data to the client.


Update (Jan 23, 2024)

We have identified a way to pass trace data from Server Components to the client for page loads. It involves using the useServerInsertedHTML hook. One thing that is important to know up-front is that this hook can only be used in client components ("use client").

In an ideal world, the implementation would look as follows:

We automatically wrap all of the user's server components and make their server components render a client component which returns null (so we're not actually putting anything in the users page) but are calling userServerInsertedHTML inside to render <meta /> tags containing sentry-trace and baggage data. The <meta /> tag data can then be picked up by the browser SDK to continue the trace.

What blocks us?

  • We need to remove the Sentry SDK from the externalized packages list in Next.js itself: It is preventing us from having client-components in the SDK.
@lforst lforst added the Package: nextjs Issues related to the Sentry Nextjs SDK label Nov 15, 2023
@lforst lforst self-assigned this Nov 15, 2023
@MrLoh
Copy link

MrLoh commented Jan 15, 2024

Hi, I'm working on coming up with an error-handling strategy for a Next.js 14 app. I'm wondering why this can’t be achieved with cookies or headers today already.

@getsantry getsantry bot moved this to Waiting for: Product Owner in GitHub Issues with 👀 2 Jan 15, 2024
@lforst
Copy link
Member Author

lforst commented Jan 16, 2024

@MrLoh What do you mean exactly? Out of principle, we cannot have the SDK set cookies. Also, I don't see how cookies would help pass a value from SSR to the client. All that aside, Next.js doesn't let you write to cookies in server components.

Similar goes for headers. You generally cannot read headers from within JS for the initial document request (only exception being the Server-Timing) header. Additionally, Next.js doesn't let you write headers from within server components.

If I am missing something or you have concrete suggestions on how to solve this let me know!

@lforst
Copy link
Member Author

lforst commented Jan 17, 2024

Yesterday I stumbled on Next.js' useServerInsertedHTML which is pretty much undocumented except for being included here: https://nextjs.org/docs/app/building-your-application/styling/css-in-js#styled-components

I have a stupid idea that we can use this to solve our problems.

@lforst
Copy link
Member Author

lforst commented Apr 9, 2024

I opened an upstream PR that would solve this issue: vercel/next.js#64256

huozhi added a commit to vercel/next.js that referenced this issue May 15, 2024
…ta to the client (#64256)

### What?

This PR adds an experimental option `clientTraceMetadata` that will use
the existing OpenTelemetry functionality to propagate conventional
OpenTelemetry trace information to the client.

The propagation metadata is propagated to the client via meta tags,
having a `name` and a `content` attribute containing the value of the
tracing value:

```html
<html>
    <head>
        <meta name="baggage" content="key1=val1,key2=val2">
        <meta name="traceparent" content="00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01">
        <meta name="custom" content="foobar">
    </head>
</html>
```

The implementation adheres to OpenTelemetry as much as possible,
treating the meta tags as if they were tracing headers on outgoing
requests. The `clientTraceMetadata` will contain the keys of the
metadata that're going to injected for tracing purpose.

### Why?

Telemetry providers usually want to provide visibility across the entire
stack, meaning it is useful for users to be able to associate, for
example, web vitals on the client, with a span tree on the server. In
order to be able to correlate tracing events from the front- and
backend, it is necessary to share something like a trace ID or similar,
that the telemetry providers can pick up and stitch back together to
create a trace.

### How?

The tracer was extended with a method `getTracePropagationData()` that
returns the propagation data on the currently active OpenTelemetry
context.
We are using `makeGetServerInsertedHTML()` to inject the meta tags into
the HTML head for dynamic requests.
The meta tags are generated through using the newly added
`getTracePropagationData()` method on the tracer.

It is important to mention that **the trace information should only be
propagated for the initial loading of the page, including hard
navigations**. Any subsequent operations should not propagate trace data
from the server to the client, as the client generally is the root of
the trace. The exception is initial pageloads, since while the request
starts on the client, no JS has had the opportunity to run yet, meaning
there is no trace propagation on the client before the server hasn't
responded.

Situations that we do not want tracing information to be propagated from
the server to the client:
- _Prefetch requests._ Prefetches generally start on the client and are
already instrumented.
- _Any sort of static precomputation, including PPR._ If we include
trace information in static pages, it means that all clients that will
land on the static page will be part of the "precomputation" trace. This
would lead to gigantic traces with a ton of unrelated data that is not
useful. The special case is dev mode where it is likely fine to
propagate trace information, even for static content, since it is
usually not actually static in dev mode.
- _Clientside (soft) navigations._ Navigations start on the client and
are usually already instrumented.

### Alternatives considered

An implementation that purely lives in user-land could have been
implemented with `useServerInsertedHTML()`, however, that implementation
would be cumbersome for users to set up, since the implementation of
tracing would have to happen in a) the instrumentation hook, b) in a
client-component that is used in a top-level layout.

### Related issues/discussions

- #47660
- #62353 (Could be used as
an alternative to the server-timing header)
- getsentry/sentry-javascript#9571

---------

Co-authored-by: Jiachi Liu <[email protected]>
panteliselef pushed a commit to panteliselef/next.js that referenced this issue May 20, 2024
…ta to the client (vercel#64256)

### What?

This PR adds an experimental option `clientTraceMetadata` that will use
the existing OpenTelemetry functionality to propagate conventional
OpenTelemetry trace information to the client.

The propagation metadata is propagated to the client via meta tags,
having a `name` and a `content` attribute containing the value of the
tracing value:

```html
<html>
    <head>
        <meta name="baggage" content="key1=val1,key2=val2">
        <meta name="traceparent" content="00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01">
        <meta name="custom" content="foobar">
    </head>
</html>
```

The implementation adheres to OpenTelemetry as much as possible,
treating the meta tags as if they were tracing headers on outgoing
requests. The `clientTraceMetadata` will contain the keys of the
metadata that're going to injected for tracing purpose.

### Why?

Telemetry providers usually want to provide visibility across the entire
stack, meaning it is useful for users to be able to associate, for
example, web vitals on the client, with a span tree on the server. In
order to be able to correlate tracing events from the front- and
backend, it is necessary to share something like a trace ID or similar,
that the telemetry providers can pick up and stitch back together to
create a trace.

### How?

The tracer was extended with a method `getTracePropagationData()` that
returns the propagation data on the currently active OpenTelemetry
context.
We are using `makeGetServerInsertedHTML()` to inject the meta tags into
the HTML head for dynamic requests.
The meta tags are generated through using the newly added
`getTracePropagationData()` method on the tracer.

It is important to mention that **the trace information should only be
propagated for the initial loading of the page, including hard
navigations**. Any subsequent operations should not propagate trace data
from the server to the client, as the client generally is the root of
the trace. The exception is initial pageloads, since while the request
starts on the client, no JS has had the opportunity to run yet, meaning
there is no trace propagation on the client before the server hasn't
responded.

Situations that we do not want tracing information to be propagated from
the server to the client:
- _Prefetch requests._ Prefetches generally start on the client and are
already instrumented.
- _Any sort of static precomputation, including PPR._ If we include
trace information in static pages, it means that all clients that will
land on the static page will be part of the "precomputation" trace. This
would lead to gigantic traces with a ton of unrelated data that is not
useful. The special case is dev mode where it is likely fine to
propagate trace information, even for static content, since it is
usually not actually static in dev mode.
- _Clientside (soft) navigations._ Navigations start on the client and
are usually already instrumented.

### Alternatives considered

An implementation that purely lives in user-land could have been
implemented with `useServerInsertedHTML()`, however, that implementation
would be cumbersome for users to set up, since the implementation of
tracing would have to happen in a) the instrumentation hook, b) in a
client-component that is used in a top-level layout.

### Related issues/discussions

- vercel#47660
- vercel#62353 (Could be used as
an alternative to the server-timing header)
- getsentry/sentry-javascript#9571

---------

Co-authored-by: Jiachi Liu <[email protected]>
@lforst
Copy link
Member Author

lforst commented May 27, 2024

Resolved by #12157

@lforst lforst closed this as completed May 27, 2024
ForsakenHarmony pushed a commit to vercel/next.js that referenced this issue Aug 14, 2024
…ta to the client (#64256)

This PR adds an experimental option `clientTraceMetadata` that will use
the existing OpenTelemetry functionality to propagate conventional
OpenTelemetry trace information to the client.

The propagation metadata is propagated to the client via meta tags,
having a `name` and a `content` attribute containing the value of the
tracing value:

```html
<html>
    <head>
        <meta name="baggage" content="key1=val1,key2=val2">
        <meta name="traceparent" content="00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01">
        <meta name="custom" content="foobar">
    </head>
</html>
```

The implementation adheres to OpenTelemetry as much as possible,
treating the meta tags as if they were tracing headers on outgoing
requests. The `clientTraceMetadata` will contain the keys of the
metadata that're going to injected for tracing purpose.

Telemetry providers usually want to provide visibility across the entire
stack, meaning it is useful for users to be able to associate, for
example, web vitals on the client, with a span tree on the server. In
order to be able to correlate tracing events from the front- and
backend, it is necessary to share something like a trace ID or similar,
that the telemetry providers can pick up and stitch back together to
create a trace.

The tracer was extended with a method `getTracePropagationData()` that
returns the propagation data on the currently active OpenTelemetry
context.
We are using `makeGetServerInsertedHTML()` to inject the meta tags into
the HTML head for dynamic requests.
The meta tags are generated through using the newly added
`getTracePropagationData()` method on the tracer.

It is important to mention that **the trace information should only be
propagated for the initial loading of the page, including hard
navigations**. Any subsequent operations should not propagate trace data
from the server to the client, as the client generally is the root of
the trace. The exception is initial pageloads, since while the request
starts on the client, no JS has had the opportunity to run yet, meaning
there is no trace propagation on the client before the server hasn't
responded.

Situations that we do not want tracing information to be propagated from
the server to the client:
- _Prefetch requests._ Prefetches generally start on the client and are
already instrumented.
- _Any sort of static precomputation, including PPR._ If we include
trace information in static pages, it means that all clients that will
land on the static page will be part of the "precomputation" trace. This
would lead to gigantic traces with a ton of unrelated data that is not
useful. The special case is dev mode where it is likely fine to
propagate trace information, even for static content, since it is
usually not actually static in dev mode.
- _Clientside (soft) navigations._ Navigations start on the client and
are usually already instrumented.

An implementation that purely lives in user-land could have been
implemented with `useServerInsertedHTML()`, however, that implementation
would be cumbersome for users to set up, since the implementation of
tracing would have to happen in a) the instrumentation hook, b) in a
client-component that is used in a top-level layout.

- #47660
- #62353 (Could be used as
an alternative to the server-timing header)
- getsentry/sentry-javascript#9571

---------

Co-authored-by: Jiachi Liu <[email protected]>
ForsakenHarmony pushed a commit to vercel/next.js that referenced this issue Aug 15, 2024
…ta to the client (#64256)

This PR adds an experimental option `clientTraceMetadata` that will use
the existing OpenTelemetry functionality to propagate conventional
OpenTelemetry trace information to the client.

The propagation metadata is propagated to the client via meta tags,
having a `name` and a `content` attribute containing the value of the
tracing value:

```html
<html>
    <head>
        <meta name="baggage" content="key1=val1,key2=val2">
        <meta name="traceparent" content="00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01">
        <meta name="custom" content="foobar">
    </head>
</html>
```

The implementation adheres to OpenTelemetry as much as possible,
treating the meta tags as if they were tracing headers on outgoing
requests. The `clientTraceMetadata` will contain the keys of the
metadata that're going to injected for tracing purpose.

Telemetry providers usually want to provide visibility across the entire
stack, meaning it is useful for users to be able to associate, for
example, web vitals on the client, with a span tree on the server. In
order to be able to correlate tracing events from the front- and
backend, it is necessary to share something like a trace ID or similar,
that the telemetry providers can pick up and stitch back together to
create a trace.

The tracer was extended with a method `getTracePropagationData()` that
returns the propagation data on the currently active OpenTelemetry
context.
We are using `makeGetServerInsertedHTML()` to inject the meta tags into
the HTML head for dynamic requests.
The meta tags are generated through using the newly added
`getTracePropagationData()` method on the tracer.

It is important to mention that **the trace information should only be
propagated for the initial loading of the page, including hard
navigations**. Any subsequent operations should not propagate trace data
from the server to the client, as the client generally is the root of
the trace. The exception is initial pageloads, since while the request
starts on the client, no JS has had the opportunity to run yet, meaning
there is no trace propagation on the client before the server hasn't
responded.

Situations that we do not want tracing information to be propagated from
the server to the client:
- _Prefetch requests._ Prefetches generally start on the client and are
already instrumented.
- _Any sort of static precomputation, including PPR._ If we include
trace information in static pages, it means that all clients that will
land on the static page will be part of the "precomputation" trace. This
would lead to gigantic traces with a ton of unrelated data that is not
useful. The special case is dev mode where it is likely fine to
propagate trace information, even for static content, since it is
usually not actually static in dev mode.
- _Clientside (soft) navigations._ Navigations start on the client and
are usually already instrumented.

An implementation that purely lives in user-land could have been
implemented with `useServerInsertedHTML()`, however, that implementation
would be cumbersome for users to set up, since the implementation of
tracing would have to happen in a) the instrumentation hook, b) in a
client-component that is used in a top-level layout.

- #47660
- #62353 (Could be used as
an alternative to the server-timing header)
- getsentry/sentry-javascript#9571

---------

Co-authored-by: Jiachi Liu <[email protected]>
ForsakenHarmony pushed a commit to vercel/next.js that referenced this issue Aug 16, 2024
…ta to the client (#64256)

This PR adds an experimental option `clientTraceMetadata` that will use
the existing OpenTelemetry functionality to propagate conventional
OpenTelemetry trace information to the client.

The propagation metadata is propagated to the client via meta tags,
having a `name` and a `content` attribute containing the value of the
tracing value:

```html
<html>
    <head>
        <meta name="baggage" content="key1=val1,key2=val2">
        <meta name="traceparent" content="00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01">
        <meta name="custom" content="foobar">
    </head>
</html>
```

The implementation adheres to OpenTelemetry as much as possible,
treating the meta tags as if they were tracing headers on outgoing
requests. The `clientTraceMetadata` will contain the keys of the
metadata that're going to injected for tracing purpose.

Telemetry providers usually want to provide visibility across the entire
stack, meaning it is useful for users to be able to associate, for
example, web vitals on the client, with a span tree on the server. In
order to be able to correlate tracing events from the front- and
backend, it is necessary to share something like a trace ID or similar,
that the telemetry providers can pick up and stitch back together to
create a trace.

The tracer was extended with a method `getTracePropagationData()` that
returns the propagation data on the currently active OpenTelemetry
context.
We are using `makeGetServerInsertedHTML()` to inject the meta tags into
the HTML head for dynamic requests.
The meta tags are generated through using the newly added
`getTracePropagationData()` method on the tracer.

It is important to mention that **the trace information should only be
propagated for the initial loading of the page, including hard
navigations**. Any subsequent operations should not propagate trace data
from the server to the client, as the client generally is the root of
the trace. The exception is initial pageloads, since while the request
starts on the client, no JS has had the opportunity to run yet, meaning
there is no trace propagation on the client before the server hasn't
responded.

Situations that we do not want tracing information to be propagated from
the server to the client:
- _Prefetch requests._ Prefetches generally start on the client and are
already instrumented.
- _Any sort of static precomputation, including PPR._ If we include
trace information in static pages, it means that all clients that will
land on the static page will be part of the "precomputation" trace. This
would lead to gigantic traces with a ton of unrelated data that is not
useful. The special case is dev mode where it is likely fine to
propagate trace information, even for static content, since it is
usually not actually static in dev mode.
- _Clientside (soft) navigations._ Navigations start on the client and
are usually already instrumented.

An implementation that purely lives in user-land could have been
implemented with `useServerInsertedHTML()`, however, that implementation
would be cumbersome for users to set up, since the implementation of
tracing would have to happen in a) the instrumentation hook, b) in a
client-component that is used in a top-level layout.

- #47660
- #62353 (Could be used as
an alternative to the server-timing header)
- getsentry/sentry-javascript#9571

---------

Co-authored-by: Jiachi Liu <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Package: nextjs Issues related to the Sentry Nextjs SDK
Projects
Archived in project
Development

No branches or pull requests

2 participants