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

Adding diagnostics channels to Fetch #2701

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
57 changes: 57 additions & 0 deletions docs/docs/api/DiagnosticsChannel.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,60 @@ diagnosticsChannel.channel('undici:websocket:pong').subscribe(({ payload }) => {
console.log(payload)
})
```
The below channels collectively act as [`tracingChannel.tracePromise`](https://nodejs.org/api/diagnostics_channel.html#tracingchanneltracepromisefn-context-thisarg-args) on `fetch`. So all of them will publish the arguments passed to `fetch`.

## `tracing:undici:fetch:start`

This message is published when `fetch` is called, and will publish the arguments passed to `fetch`.

```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('tracing:undici:fetch:start').subscribe(({ req, input, init, }) => {
console.log('input', input)
console.log('init', init)
})
```

## `tracing:undici:fetch:end`

This message is published at the end of `fetch`'s execution, and will publish any `error` from the synchronous part of `fetch`. Since `fetch` is asynchronous, this should be empty. This channel will publish the same values as `undici:fetch:start`, but we are including it to track when `fetch` finishes execution and to be consistent with [`TracingChannel`](https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel).
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('tracing:undici:fetch:end').subscribe(({ req, input, init, error }) => {
console.log('input', input)
console.log('init', init)
console.log('error', error) // should be empty
})
```
## `tracing:undici:fetch:asyncStart`
This message is published after `fetch` resolves or rejects. If `fetch` resolves, it publishes the response in `result`. If it rejects, it publishes the error in `error`.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('tracing:undici:fetch:asyncStart').subscribe(({ req, input, init, result, error }) => {
console.log('input', input)
console.log('init', init)
console.log('response', result)
console.log('error', error)
})
```
## `tracing:undici:fetch:asyncEnd`
This channel gets published the same values as and at the same time as `tracing:undici:fetch:asyncStart` in the case of [`tracingChannel.tracePromise`](https://nodejs.org/api/diagnostics_channel.html#tracingchanneltracepromisefn-context-thisarg-args)
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('tracing:undici:fetch:asyncEnd').subscribe(({ req, input, init, result, error }) => {
console.log('input', input)
console.log('init', init)
console.log('response', result)
console.log('error', error)
})
```
## `tracing:undici:fetch:error`
This message is published when an error is thrown or promise rejects while calling `fetch`.
```js
import diagnosticsChannel from 'diagnostics_channel'
diagnosticsChannel.channel('tracing:undici:fetch:error').subscribe(({ req, input, init, error }) => {
console.log('input', input)
console.log('init', init)
console.log('error', error)
})
```
79 changes: 78 additions & 1 deletion lib/core/diagnostics.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ const undiciDebugLog = util.debuglog('undici')
const fetchDebuglog = util.debuglog('fetch')
const websocketDebuglog = util.debuglog('websocket')
let isClientSet = false
let tracingChannel

if (diagnosticsChannel.tracingChannel) {
tracingChannel = diagnosticsChannel.tracingChannel('undici:fetch')
}

const channels = {
// Client
beforeConnect: diagnosticsChannel.channel('undici:client:beforeConnect'),
Expand All @@ -23,7 +29,9 @@ const channels = {
close: diagnosticsChannel.channel('undici:websocket:close'),
socketError: diagnosticsChannel.channel('undici:websocket:socket_error'),
ping: diagnosticsChannel.channel('undici:websocket:ping'),
pong: diagnosticsChannel.channel('undici:websocket:pong')
pong: diagnosticsChannel.channel('undici:websocket:pong'),
// Fetch channels
tracingChannel
}

if (undiciDebugLog.enabled || fetchDebuglog.enabled) {
Expand Down Expand Up @@ -114,6 +122,75 @@ if (undiciDebugLog.enabled || fetchDebuglog.enabled) {
isClientSet = true
}

// Track fetch requests
if (fetchDebuglog.enabled && diagnosticsChannel.tracingChannel) {
const debuglog = fetchDebuglog

tracingChannel.start.subscribe(evt => {
const {
input
} = evt
debuglog(
'fetch has started request to %s',
input
)
})

tracingChannel.end.subscribe(evt => {
const {
input
} = evt
debuglog(
'fetch has received response from %s',
input
)
})

tracingChannel.asyncStart.subscribe(evt => {
const {
input,
result,
error
} = evt
if (result && error) {
debuglog(
'fetch has received response for %s - HTTP %d, error is %s',
input,
result.status,
error.message
)
} else if (result) {
debuglog(
'fetch has received response for %s - HTTP %d',
input,
result.status
)
} else if (error) {
debuglog(
'fetch has errored for %s - %s',
input,
error.message
)
} else {
debuglog(
'fetch has started request to %s',
input
)
}
})

tracingChannel.error.subscribe(evt => {
const {
error
} = evt
debuglog(
'fetch error event received response %s',
error.message
)
})
isClientSet = true
}

if (websocketDebuglog.enabled) {
if (!isClientSet) {
const debuglog = undiciDebugLog.enabled ? undiciDebugLog : websocketDebuglog
Expand Down