-
Notifications
You must be signed in to change notification settings - Fork 309
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
Adds support for undici tracing #1642
Conversation
18b1cfa
to
a8feda6
Compare
@Qard , any suggestion regarding the node-ext and upstream koa failures? Is my setup wrong? |
I'm pretty sure it's due to next.js tests trying to bundle it when it shouldn't. I believe there was a place to exclude things, but I forget where exactly. @rochdev might know how to deal with that. |
Got bitten by this; https://github.com/nodejs/node/blob/v16.11.1/lib/diagnostics_channel.js#L101-L103; the channels are weak ref'd and I've spent about 2 days trying to understand why my tracing would not work while deployed and was working from the REPL / tests 😮💨 |
For upstream
We were also bitten by this while implementing |
glad to know I'm not alone! Will rebase and see how it goes! |
ae925f6
to
206ff99
Compare
Hi there! Any update on this one? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the late reply as we've been trying to move the plugin system to diagnostics_channel
and figuring out the details of how to properly use it ourselves.
It turns out that undici
cannot be instrumented using DC in its current state because it doesn't have the proper channels/events for context propagation, which we need for spans to be connected together properly and for tracking the execution and control flows. This is something we'll have to work with @mcollina to support for both undici
and fastify
when we have finalized the design. The short version is that in order for AsyncLocalStorage
to be used properly, there must be points at which to call enterWith()
both at the beginning and synchronous end of a function for a client. Without this we cannot activate the span properly without leaking to the callback. So we need a minimum of 4 events (start
, end
, async-end
, error
) and undici
only implements 3.
In the meantime, can you alter the integration to monkey-patch undici
instead of using DC? It's not ideal, but unfortunately it's the only way short-term, and it will have the benefit of supporting older versions as well. At least most of the current logic should remain untouched other than the instrumentation points.
const REFERENCE_CHILD_OF = opentracing.REFERENCE_CHILD_OF | ||
const REFERENCE_NOOP = constants.REFERENCE_NOOP | ||
|
||
function addError (span, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of code including this one seems to have been copied from the http
plugin. Can it all be deduplicated with some sort of helper? Other helpers can be found in the plugins/util
folder, so for example adding a http.js
.
For this one specifically, it shouldn't even be needed at all since span.setTag('error', error)
now handles this logic for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perfect! Let me have a look at how I can get the implementation going for the monkey patch as well.
I do not have the time to look into it right now, but adding the missing one is just a PR away. It would probably take less time and be more maintenance friendly to just do that. |
@rochdev , if I recap, in order to use diagnostics_channels, we need to implement the event handling through async storage. |
@flovilmart The events that are needed are basically:
Additionally, while promises are handled automatically by So an example fake const { AsyncResource } = require('async_hooks')
function request (callback) {
const req = {}
callback = AsyncResource.bind(callback)
startChannel.publish(req)
doActualAsyncRequest(req, (err, res) => {
if (err) {
errorChannel.publish(err)
}
asyncEndChannel.publish(req)
callback(req, res)
})
endChannel.publish(req)
} |
Oh great! Thanks @rochdev ; let me see how I can get this implemented. |
@rochdev , I'm curious; and perhaps the current implementation is completely wrong, but given that this implementation keeps a WeakMap of |
@flovilmart The context is never activated in the current implementation, unlike http, so any child of Unfortunately these are issues that are much easier to solve with monkey-patching, but since We're also working on a doc to explain how to do tracing using DC, especially since it's much more involved to get it right compared to monkey-patching, which makes it more difficult to use for external contributors. |
Thanks a lot @rochdev for taking the time for this explanation! I concur that monkey patching is more straightforward then! Let me reevaluate both approaches. |
@flovilmart Sorry for the runaround with this PR. We hadn't quite realized that the current events were incomplete when we recommended trying to use DC for this. Let me know if you need any help to bring this to the finish line or have any questions about either approach. |
@rochdev no problem! All's good; I did not realize we were missing so many events. |
Does it mean the "async-end" should be emitted when body is consumed (read end from socket) ? |
@artur-ma According to the current implementation for |
@artur-ma sure, this doesn't include body reading probably it would need it's own span outside of the request span. What do you think? I'm saying outside, as some users may not even read the body, but simply rely on the headers. |
206ff99
to
f50f366
Compare
const span = tracer.startSpan('http.request', { | ||
childOf, | ||
tags: { | ||
[SPAN_KIND]: CLIENT | ||
} | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we init the span early and pass it through the asyncLocalStorage. not sure this is exactly what was intended
'http.url': uri, | ||
'service.name': getServiceName(tracer, config, request) | ||
}) | ||
requestSpansMap.set(request, span) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we keep the request in the WeakMap as for some reason, they're not available in the subsequent diagnostics channels events. Which makes me wonder why? and also if we can rely on this span to exist in the undici:request:create
event
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you open an issue about this? Or a PR to add them? saving the WeakMap will greatly reduce overhead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you mean. Is that more efficient to store through the weak map than the async context?
or would you want something on the request object to add external context in the form of a weak map?
e0f306f
to
303b2f4
Compare
cab97c9
to
e66468a
Compare
@rochdev I believe I made some strides in the right direction:
I also extracted commons from the ❓ If we want to be extensive, we should run the whole suite with all methods we'll probably find some wholes in there. The instrumentation is a bit all over the place with the 2 wrapping strategies, I could attempt to simplify a bit 😅. let me know what you think. |
c7f580f
to
f2ea453
Compare
2f1eb34
to
b6fdb97
Compare
@mcollina how would you approach instrumenting reading the response body? Is there a single member that I can wrap on the response and watch for an |
This is a bit tricky and I don't have the solution at the top of my head without digging deep into the code. What I would try first is to add one event listeners for the |
Thanks for the tip @mcollina ! |
closing as another implementation is coming. |
Hey, there @flovilmart Just wanted to check in on this. Any updates on the new implementation? Thanks. |
What does this PR do?
This PR adds support for tracing undici, for the upcoming release.
Closes #1615
Motivation
Undici does not use the standard HTTP / HTTPS modules from node.js.
This is required so we can properly trace undici requests.
Plugin Checklist
Additional Notes