Skip to content

Commit

Permalink
feat(fastify): add requestHook support
Browse files Browse the repository at this point in the history
The `requestHook` config option allows custom span handling per request
layer.
  • Loading branch information
luismiramirez committed Oct 26, 2022
1 parent 23589d6 commit a6c063f
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 4 deletions.
22 changes: 22 additions & 0 deletions plugins/node/opentelemetry-instrumentation-fastify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,28 @@ registerInstrumentations({

See [examples/fastify](https://github.com/open-telemetry/opentelemetry-js-contrib/tree/main/examples/fastify) for a short example.

## Fastify Instrumentation Options

| Options | Type | Example | Description |
| `requestHook` | `FastifyCustomAttributeFunction` | `(span, request) => {}` | Function for adding custom attributes to Fastify layers. Receives params: `Span, FastifyRequest`. |

#### Using `requestHook`

Instrumentation configuration accepts a custom "hook" function which will be called for every instrumented Fastify layer involved in a request. Custom attributes can be set on the span or run any custom logic per layer.

```javascript
import { FastifyInstrumentation } from "@opentelemetry/instrumentation-fastify"

const fastifyInstrumentation = new FastifyInstrumentation({
requestHook: function (span: Span, request: FastifyRequest) {
span.setAttribute(
'http.method',
request.method,
)
}
});
```

## Useful links

- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@
"@types/express": "4.17.13",
"@types/mocha": "7.0.2",
"@types/node": "16.11.21",
"gts": "3.1.0",
"@types/sinon": "10.0.13",
"fastify": "^4.5.3",
"gts": "3.1.0",
"mocha": "7.2.0",
"nyc": "15.1.0",
"rimraf": "3.0.2",
"sinon": "14.0.1",
"ts-mocha": "10.0.0",
"typescript": "4.3.5"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

import {
context,
diag,
SpanAttributes,
SpanStatusCode,
trace,
} from '@opentelemetry/api';
import { getRPCMetadata, RPCType } from '@opentelemetry/core';
import {
InstrumentationBase,
InstrumentationConfig,
InstrumentationNodeModuleDefinition,
safeExecuteInTheMiddle,
} from '@opentelemetry/instrumentation';
Expand All @@ -38,7 +38,11 @@ import {
FastifyNames,
FastifyTypes,
} from './enums/AttributeNames';
import type { HandlerOriginal, PluginFastifyReply } from './types';
import type {
HandlerOriginal,
PluginFastifyReply,
FastifyInstrumentationConfig,
} from './types';
import {
endSpan,
safeExecuteInTheMiddleMaybePromise,
Expand All @@ -50,14 +54,22 @@ export const ANONYMOUS_NAME = 'anonymous';

/** Fastify instrumentation for OpenTelemetry */
export class FastifyInstrumentation extends InstrumentationBase {
constructor(config: InstrumentationConfig = {}) {
constructor(config: FastifyInstrumentationConfig = {}) {
super(
'@opentelemetry/instrumentation-fastify',
VERSION,
Object.assign({}, config)
);
}

override setConfig(config: FastifyInstrumentationConfig = {}) {
this._config = Object.assign({}, config);
}

override getConfig(): FastifyInstrumentationConfig {
return this._config as FastifyInstrumentationConfig;
}

init() {
return [
new InstrumentationNodeModuleDefinition<any>(
Expand Down Expand Up @@ -271,6 +283,19 @@ export class FastifyInstrumentation extends InstrumentationBase {
spanName,
spanAttributes
);

if (instrumentation.getConfig().requestHook) {
safeExecuteInTheMiddle(
() => instrumentation.getConfig().requestHook!(span, request),
e => {
if (e) {
diag.error('fastify instrumentation: request hook failed', e);
}
},
true
);
}

return context.with(trace.setSpan(context.active(), span), () => {
done();
});
Expand Down
19 changes: 19 additions & 0 deletions plugins/node/opentelemetry-instrumentation-fastify/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,28 @@
import { Span } from '@opentelemetry/api';
import type { FastifyReply } from 'fastify';
import { spanRequestSymbol } from './constants';
import { InstrumentationConfig } from '@opentelemetry/instrumentation';
import type { FastifyRequest } from 'fastify/types/request';

export type HandlerOriginal = (() => Promise<unknown>) & (() => void);

export type PluginFastifyReply = FastifyReply & {
[spanRequestSymbol]?: Span[];
};

/**
* Function that can be used to add custom attributes to the current span
* @param span - The Fastify layer span.
* @param request - The Fastify request object.
*/
export interface FastifyCustomAttributeFunction {
(span: Span, request: FastifyRequest): void;
}

/**
* Options available for the Fastify Instrumentation
*/
export interface FastifyInstrumentationConfig extends InstrumentationConfig {
/** Function for adding custom attributes to each layer span */
requestHook?: FastifyCustomAttributeFunction;
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ import {
ReadableSpan,
SimpleSpanProcessor,
} from '@opentelemetry/sdk-trace-base';
import { Span } from '@opentelemetry/api';
import { HookHandlerDoneFunction } from 'fastify/types/hooks';
import { FastifyReply } from 'fastify/types/reply';
import { FastifyRequest } from 'fastify/types/request';
import * as http from 'http';
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
import { ANONYMOUS_NAME } from '../src/instrumentation';
import { AttributeNames, FastifyInstrumentation } from '../src';
import * as sinon from 'sinon';

const URL = require('url').URL;

Expand Down Expand Up @@ -426,5 +428,36 @@ describe('fastify', () => {
await startServer();
});
});

describe('using requestHook in config', () => {
it('calls requestHook provided function when set in config', async () => {
const requestHook = sinon.spy((span: Span, request: FastifyRequest) => {
span.setAttribute('http.method', request.method);
});

instrumentation.setConfig({
...instrumentation.getConfig(),
requestHook
});

app.get('/test', (req, res) => {
res.send('OK');
});

await startServer();
await httpRequest.get(`http://localhost:${PORT}/test`);

const spans = memoryExporter.getFinishedSpans();
assert.strictEqual(spans.length, 5);
const span = spans[3];
assert.deepStrictEqual(span.attributes, {
'fastify.type': 'request_handler',
'plugin.name': 'fastify -> @fastify/express',
[SemanticAttributes.HTTP_ROUTE]: '/test',
'http.method': 'GET',
});
})
});
});
});

0 comments on commit a6c063f

Please sign in to comment.