Skip to content

Commit

Permalink
add diagnostics_channel publish at initialization time (#3279)
Browse files Browse the repository at this point in the history
  • Loading branch information
bengl committed Aug 27, 2021
1 parent 6ded7ff commit a04f4c9
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 0 deletions.
42 changes: 42 additions & 0 deletions docs/Hooks.md
Expand Up @@ -564,3 +564,45 @@ fastify.route({
```

**Note**: both options also accept an array of functions.

## Diagnostics Channel Hooks

> **Note:** The `diagnostics_channel` is currently experimental on Node.js, so
> its API is subject to change even in semver-patch releases of Node.js. For
> versions of Node.js supported by Fastify where `diagnostics_channel` is
> unavailable, the hook will use the
> [polyfill](https://www.npmjs.com/package/diagnostics_channel) if it is
> available. Otherwise this feature will not be present.
Currently, one
[`diagnostics_channel`](https://nodejs.org/api/diagnostics_channel.html) publish
event, `'fastify.initialization'`, happens at initialization time. The Fastify
instance is passed into the hook as a property of the object passed in. At this
point, the instance can be interacted with to add hooks, plugins, routes or any
other sort of modification.

For example, a tracing package might do something like the following (which is,
of course, a simplification). This would be in a file loaded in the
initialization of the tracking package, in the typical "require instrumentation
tools first" fashion.

```js
const tracer = /* retrieved from elsehwere in the package */
const dc = require('diagnostics_channel')
const channel = dc.channel('fastify.initialization')
const spans = new WeakMap()

channel.subscribe(function ({ fastify }) {
fastify.addHook('onRequest', (request, reply, done) => {
const span = tracer.startSpan('fastify.request')
spans.set(request, span)
done()
})

fastify.addHook('onResponse', (request, reply, done) => {
const span = spans.get(request)
span.finish()
done()
})
})
```
11 changes: 11 additions & 0 deletions fastify.js
Expand Up @@ -399,6 +399,17 @@ function fastify (options) {
// Delay configuring clientError handler so that it can access fastify state.
server.on('clientError', options.clientErrorHandler.bind(fastify))

try {
const dc = require('diagnostics_channel')
const initChannel = dc.channel('fastify.initialization')
if (initChannel.hasSubscribers) {
initChannel.publish({ fastify })
}
} catch (e) {
// This only happens if `diagnostics_channel` isn't available, i.e. earlier
// versions of Node.js. In that event, we don't care, so ignore the error.
}

return fastify

function throwIfAlreadyStarted (msg) {
Expand Down
61 changes: 61 additions & 0 deletions test/diagnostics-channel.test.js
@@ -0,0 +1,61 @@
'use strict'

const t = require('tap')
const test = t.test
const proxyquire = require('proxyquire')

test('diagnostics_channel when present and subscribers', t => {
t.plan(3)

let fastifyInHook

const dc = {
channel (name) {
t.equal(name, 'fastify.initialization')
return {
hasSubscribers: true,
publish (event) {
t.ok(event.fastify)
fastifyInHook = event.fastify
}
}
},
'@noCallThru': true
}

const fastify = proxyquire('../fastify', {
diagnostics_channel: dc
})()
t.equal(fastifyInHook, fastify)
})

test('diagnostics_channel when present and no subscribers', t => {
t.plan(1)

const dc = {
channel (name) {
t.equal(name, 'fastify.initialization')
return {
hasSubscribers: false,
publish () {
t.fail('publish should not be called')
}
}
},
'@noCallThru': true
}

proxyquire('../fastify', {
diagnostics_channel: dc
})()
})

test('diagnostics_channel when not present', t => {
t.plan(1)

t.doesNotThrow(() => {
proxyquire('../fastify', {
diagnostics_channel: null
})()
})
})

0 comments on commit a04f4c9

Please sign in to comment.