Skip to content

Make README example less pseudo-y #69

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

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
152 changes: 117 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,35 +29,133 @@ It must be configured before defining routes and other plugins in order to cover
- `onResponse`
- `onError`
- Instruments automatically custom 404 Not Found handler
- TODO: Clarify server shutdown/cleanup around otel metrics/traces

Example:
## Example
Copy link
Member

Choose a reason for hiding this comment

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

all this code could be moved to an example.js file instead of the readme?

it seems too much here to me (and we can't test it too)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. Will add in a runnable example in.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I reduced the scope of the example in the README. I will still set up an external example folder.


```js
// ... in your OTEL setup
const FastifyOtelInstrumentation = require('@fastify/otel');
// otel.js
// Sets up metrics, tracing, HTTP & Node runtime instrumentation, and host metrics.
// Uses ConsoleSpanExporter for local debugging of spans.
const { NodeSDK } = require('@opentelemetry/sdk-node')
const { ConsoleSpanExporter } = require('@opentelemetry/sdk-trace-node')
const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base')
const { PrometheusExporter } = require('@opentelemetry/exporter-prometheus')
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http')
const { FastifyOtelInstrumentation } = require('@fastify/otel')
const { metrics, trace } = require('@opentelemetry/api')
const { resourceFromAttributes } = require('@opentelemetry/resources')
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions')

// Set up your preferred processors and exporters
const traceExporter = new ConsoleSpanExporter()
const spanProcessor = new SimpleSpanProcessor(traceExporter)
const prometheusExporter = new PrometheusExporter({ port: 9091 })

const sdk = new NodeSDK({
resource: resourceFromAttributes({
// This can also be set by OTEL_SERVICE_NAME
// Instruments inherit from the SDK resource attributes
[SemanticResourceAttributes.SERVICE_NAME]: 'my-service-name',
}),
spanProcessor,
metricReader: prometheusExporter,
instrumentations: [
// HttpInstrumentation is required for FastifyOtelInstrumentation to work
new HttpInstrumentation(),
new FastifyOtelInstrumentation({
// Automatically register the @fastify/otel fastify plugin for all routes
registerOnInitialization: true,
})
],
})

sdk.start()
module.exports = { sdk }

process.on('SIGTERM', () => {
// @fastify/otel doesn't set up graceful shutdown of span processing
sdk.shutdown()
.then(
() => console.log('SDK shut down successfully'),
(err) => console.log(new Error('Error shutting down SDK', { cause: err }))
)
})
```

// If serverName is not provided, it will fallback to OTEL_SERVICE_NAME
// as per https://opentelemetry.io/docs/languages/sdk-configuration/general/.
const fastifyOtelInstrumentation = new FastifyOtelInstrumentation({ servername: '<yourCustomApplicationName>' });
fastifyOtelInstrumentation.setTracerProvider(provider)
Otel recommends using the loader/require flag for loading your otel setup file.
For ESM ("type"="module"), you must also pass a `--experimental-loader` flag.

module.exports = { fastifyOtelInstrumentation }
[Fastify-cli](https://github.com/fastify/fastify-cli):

// ... in your Fastify definition
const { fastifyOtelInstrumentation } = require('./otel.js');
const Fastify = require('fastify');
- `NODE_OPTIONS='--import otel.js --experimental-loader=@opentelemetry/instrumentation/hook.mjs' fastify start` for esm
- `fastify start --require otel.js` for cjs

const app = fastify();
Node.js:

- `node --experimental-loader=@opentelemetry/instrumentation/hook.mjs --import ./otel.js ./app.js` for esm
- `node --require ./otel.js ./app.js` for cjs

```js
// app.js
import Fastify from 'fastify';

// Because we used a loader flag and registerOnInitialization=true
// All routes will automatically be insturmented
const app = await Fastify();

app.get('/', async () => 'hello world');

app.get(
'/healthcheck',
{ config: { otel: false } }, // skip tracing/metrics for this route
async () => 'Up!'
);

// example of encapsulated context with its own instrumentation
app.register(async (instance) => {
// will inherit global FastifyOtelInstrumentation because we registered with
// registerOnInitialization
instance.get('/', async () => 'nested hello world');
}, { prefix: '/nested' });

await app.listen({ port: 3000 });
console.log('⚡ Fastify listening on http://localhost:3000');
````

### Manual plugin registration

You can also export your fastifyOtelInstrumentation to register the plugin on specific routes instead of all routes.

```js
// otel.js
const fastifyOtelInstrumentation = new FastifyOtelInstrumentation({
// Set this to false to not automatically install the fastify plugin on all routes
registerOnInitialization: false,
})

const sdk = new NodeSDK({
// ...
instrumentations: [
// ...
fastifyOtelInstrumentation
],
})

sdk.start()
module.exports = { sdk, fastifyOtelInstrumentation }
```

```js
import Fastify from 'fastify';
import { fastifyOtelInstrumentation } from './otel.js';

const app = await Fastify();
// It is necessary to await for its register as it requires to be able
// to intercept all route definitions
await app.register(fastifyOtelInstrumentation.plugin());

// automatically all your routes will be instrumented
app.get('/', () => 'hello world')
// as well as your instance level hooks.
app.addHook('onError', () => /* do something */)
// Manually skip telemetry for a specific route
app.get('/healthcheck', { config: { otel: false } }, () => 'Up!')
app.get('/', async () => 'hello world');

// you can also scope your instrumentation to only be enabled on a sub context
// of your application
Expand All @@ -69,23 +167,7 @@ app.register((instance, opts, done) => {

done()
}, { prefix: '/nested' })
```

### Automatic plugin registration

The plugin can be automatically registered with `registerOnInitialization` option set to `true`.
In this case, it is necessary to await fastify instance.

```js
// ... in your OTEL setup
const fastifyOtelInstrumentation = new FastifyOtelInstrumentation({
registerOnInitialization: true,
});

// ... in your Fastify definition
const Fastify = require('fastify');
const app = await fastify();
```
````

> **Notes**:
>
Expand Down