Skip to content

Conversation

@Qard
Copy link
Contributor

@Qard Qard commented Jan 9, 2026

What does this PR do?

This provides now complete support for undici, not just fetch only.

Motivation

Presently the Undici instrumentation only supports the fetch(...) function, not the rest of the library. This adds full support via the diagnostics_channel events.

Plugin Checklist

Additional Notes

This provides now complete support for undici, not just fetch only.
Copy link
Collaborator

@BridgeAR BridgeAR left a comment

Choose a reason for hiding this comment

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

This is mostly looking very good! I just left some small improvement requests :)

Comment on lines +11 to +13
const HTTP_STATUS_CODE = tags.HTTP_STATUS_CODE
const HTTP_REQUEST_HEADERS = tags.HTTP_REQUEST_HEADERS
const HTTP_RESPONSE_HEADERS = tags.HTTP_RESPONSE_HEADERS
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit

Suggested change
const HTTP_STATUS_CODE = tags.HTTP_STATUS_CODE
const HTTP_REQUEST_HEADERS = tags.HTTP_REQUEST_HEADERS
const HTTP_RESPONSE_HEADERS = tags.HTTP_RESPONSE_HEADERS
const {
HTTP_STATUS_CODE,
HTTP_REQUEST_HEADERS,
HTTP_RESPONSE_HEADERS,
} = tags

// These fire for undici >= 4.7.0 for ALL request types (fetch, request, stream, etc.)
// ===========================================

_onNativeRequestCreate ({ request }) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use private properties (also for the other methods)

Comment on lines +42 to +44
const origin = request.origin || ''
const path = request.path || '/'
const method = (request.method || 'GET').toUpperCase()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const origin = request.origin || ''
const path = request.path || '/'
const method = (request.method || 'GET').toUpperCase()
const { origin = '', path = '/' } = request
const method = request.method?.toUpperCase() ?? 'GET'

}

// Store span context on request for later retrieval
request[kSpanContext] = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we just use a WeakMap instead? That would prevent the need for the manipulation

Comment on lines +140 to +150
if (response?.headers && this.config.headers) {
const headers = normalizeHeaders(response.headers)

for (const [key, tag] of this.config.headers) {
const value = headers[key]
if (value) {
span.setTag(tag || `${HTTP_RESPONSE_HEADERS}.${key}`, value)
}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we just use a small utility function that works for response and requests? That way there is less code duplication :)

const store = super.bindStart(ctx)

// Inject trace headers back into the request
for (const name in options.headers) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit

Suggested change
for (const name in options.headers) {
for (const name of Object.keys(options.headers)) {

Comment on lines +235 to +236
if (ctx.error && ctx.error.name === 'AbortError') return
return super.error(ctx)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit

Suggested change
if (ctx.error && ctx.error.name === 'AbortError') return
return super.error(ctx)
if (!ctx.error || ctx.error.name !== 'AbortError') {
return super.error(ctx)
}

assert.strictEqual(traces[0][0].meta[ERROR_MESSAGE], error.message || error.code)
assert.strictEqual(traces[0][0].meta[ERROR_STACK], error.stack)
assert.strictEqual(traces[0][0].meta.component, 'undici')
assert.strictEqual(traces[0][0].error, 1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit while being on it: could you rewrite these to use assertObjectContains(traces, [[{ meta: { ... } }]]) instead?

assert.strictEqual(traces[0][0].meta['http.method'], 'GET')
assert.strictEqual(traces[0][0].meta['http.status_code'], '200')
assert.strictEqual(traces[0][0].meta.component, 'undici')
assert.strictEqual(traces[0][0].meta['out.host'], 'localhost')
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use assertObjectContains() instead.

})
appListener = server(app, port => {
agent
.assertSomeTraces(traces => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please use agent.assertFirstTrace({ resource: 'POST', ... })

The same with other tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants