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

Use W3C trace context headers to support OTEL trace propagation #56891

Conversation

sfirrin
Copy link

@sfirrin sfirrin commented Oct 16, 2023

Adding propagation to OTEL tracing

Implements OTEL propagation https://opentelemetry.io/docs/instrumentation/js/propagation/ for API route handler traces by extracting the W3C trace context headers https://www.w3.org/TR/trace-context/ from the request and using them to create the context for the active span.

I spent some time trying to figure out how best to test this but I wasn't sure. Would appreciate a pointer to existing tests for the tracer or any advice on testing this.

I followed the doc here https://github.com/vercel/next.js/blob/canary/contributing/core/developing-using-local-app.md and was able to confirm the header values are being used to create the context through logging, but have not been able to do a more thorough test end-to-end to confirm that the generated API route spans are parented correctly. I wanted to deploy a testing app to Vercel that uses my local Next.js build so I could double check that the headers are being used in the generated traces, but I ran into some problems getting the app to build correctly with my local version of Next.js. I asked in Discord here https://discord.com/channels/752553802359505017/1007476603422527558/threads/1162417878507737228 and someone has been helping out but I still haven't yet been able to deploy it. If the reviewer has any advice on the Error: No Next.js version could be detected in your project. Make sure "next" is installed in "dependencies" or "devDependencies" error I'm getting there it would be appreciated (more context in Discord).

*I was not able to find a checklist in the PR template for this that is a great match for this since it's not exactly a bug or a feature. Please let me know if anything should be added or expanded here

@@ -711,6 +720,7 @@ export default abstract class Server<ServerOptions extends Options = Options> {
'http.method': method,
'http.target': req.url,
},
reqHeaderTraceContext: { traceParent, traceState },
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we need the intermediary context object. Also let's use the traceparent as is, without the camelcase, to follow the OTEL convention

Copy link
Author

Choose a reason for hiding this comment

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

I don't think we need the intermediary context object

To clarify, do you mean to include the traceparent and tracestate properties in the top level of this TracerSpanOptions object?

@feedthejim
Copy link
Contributor

to test it out yourself, you can run pnpm pack inside the packages/next folder and install the tarball to your project

@valkum
Copy link

valkum commented Oct 17, 2023

As someone who maintains an internal patch for this support (hadn't had time to create an upstream PR yet): Can we get some tests as well to avoid breaking this in the future?

@sfirrin
Copy link
Author

sfirrin commented Oct 17, 2023

Definitely agree @valkum, I am trying to figure out how to test this but haven't been able to find how this kind of tracer logic is tested within Next.js so would appreciate any pointer

@valkum
Copy link

valkum commented Oct 17, 2023

@@ -702,6 +702,15 @@ export default abstract class Server<ServerOptions extends Options = Options> {
): Promise<void> {
await this.prepare()
const method = req.method.toUpperCase()

// Extract the w3c trace context headers and pass them to tracer
const traceParentHeader = req.headers.traceparent
Copy link
Member

Choose a reason for hiding this comment

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

IMHO, it'd be better to either move the propagation extraction code from below to here or to pass headers to the trace call. For instance, this code could execute:

spanContext = propagation.extract(context.active(), req.headers)

And pass an optional spanContext to the trace() method. This way the SDK can configure whatever propagation strategy it likes outside of Next and it'd work.

Copy link
Member

Choose a reason for hiding this comment

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

If we don't want to pass spanContext to trace(), there should also be a way to propagate it via OpenTelemetry's context.with().

Copy link
Member

Choose a reason for hiding this comment

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

Here's the alternative, which, IMHO, should cover the target cases better: #57084

Copy link
Author

Choose a reason for hiding this comment

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

OK thanks, I was planning to work on your feedback today but I'm happy to let you take it from here with your PR

Can you help me understand how your PR is an improvement / expand on what you mean by This way the SDK can configure whatever propagation strategy it likes outside of Next and it'd work?

Copy link
Member

Choose a reason for hiding this comment

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

In a otel setup (in an application, not in Next), you could do something like this:

traceProvider.register({
  propagator: new XPropagator(),
})

E.g. you could use AWSXRayPropagator or make your own.

This propagator could work off headers or some special environment context. Next.js wouldn't need to know any of that - it'd just call a generic propagator.extract() and (eventually)propagator.inject() and delegate this part fully to the configuration of the application or a deployment environment.

Copy link
Member

Choose a reason for hiding this comment

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

And FWIW, your PR is super close, it just limits which propagators can be used.

Copy link
Author

Choose a reason for hiding this comment

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

I see, thanks for explaining and for opening your PR. I'll close this one out in favor of yours

// Extract the w3c trace context headers and pass them to tracer
const traceParentHeader = req.headers.traceparent
const traceStateHeader = req.headers.tracestate
const traceParent =
Copy link
Member

Choose a reason for hiding this comment

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

If we extract the propagation, should we also inject it in the downstream fetch calls? Or would that be too subjective?

@sfirrin
Copy link
Author

sfirrin commented Oct 23, 2023

Closing in favor of #57084 which does the same

@sfirrin sfirrin closed this Oct 23, 2023
feedthejim pushed a commit that referenced this pull request Oct 27, 2023
…7084)

Implement OTEL propagation according to the OpenTelemetry API per
https://opentelemetry.io/docs/specs/otel/context/api-propagators/. Any
configured propagator should work with this API, including
`W3CTraceContextPropagator` (https://www.w3.org/TR/trace-context/).

Alternative to the #56891.

/cc @sfirrin
@github-actions github-actions bot added the locked label Nov 7, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 7, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants