Skip to content

Commit

Permalink
feat: new fastify instrumentation (#611)
Browse files Browse the repository at this point in the history
* feat: new fastify instrumentation

* reviews

* adding error handling for unhandled exception, fixing returned constructor, http.route, context propagation

* fixing ending span, fixing tests, fixing case with more than 1 span to be end

* upgrading to latest

* fix: application hooks throws exception from instrumentation (#5)

Co-authored-by: Amir Blum <amirgiraffe@gmail.com>
  • Loading branch information
obecny and blumamir committed Oct 22, 2021
1 parent 25b93bb commit 77c215b
Show file tree
Hide file tree
Showing 29 changed files with 1,648 additions and 5 deletions.
3 changes: 2 additions & 1 deletion examples/connect/client.js
Expand Up @@ -2,7 +2,8 @@

// eslint-disable-next-line import/order
const tracing = require('./tracing')('example-connect-client');
const tracer = tracing.tracer;

const { tracer } = tracing;
const api = require('@opentelemetry/api');
const axios = require('axios').default;

Expand Down
2 changes: 1 addition & 1 deletion examples/connect/server.js
Expand Up @@ -11,6 +11,7 @@ const axios = require('axios');
const app = connect();
const PORT = 8080;

// eslint-disable-next-line prefer-arrow-callback
app.use(function middleware1(req, res, next) {
next();
});
Expand All @@ -32,7 +33,6 @@ app.use('/run_test', async (req, res) => {
tracing.log('enabling connect');
tracing.connectInstrumentation.enable();
}

});

app.listen(PORT);
2 changes: 1 addition & 1 deletion examples/connect/tracing.js
Expand Up @@ -48,5 +48,5 @@ module.exports = (serviceName) => {
connectInstrumentation,
provider,
tracer: opentelemetry.trace.getTracer('connect-example'),
}
};
};
56 changes: 56 additions & 0 deletions examples/fastify/README.md
@@ -0,0 +1,56 @@
# Overview

OpenTelemetry Fastify Instrumentation allows the user to automatically collect trace data and export them to the backend of choice (Collector Exporter), to give observability to distributed systems.

This is a simple example that demonstrates tracing calls made to Fastify API. The example shows key aspects of tracing such as
- Root Span (on Client)
- Child Span (on Client)
- Span Events
- Span Attributes

## Installation

```sh
$ # from this directory
$ npm install
```

## Run the Application

### Collector - docker container

- Run docker container with collector

```sh
# from this directory
$ npm run docker:start
```

### Server

- Run the server

```sh
# from this directory
$ npm run server
```

- Run the client

```sh
# from this directory
npm run client
```

#### Zipkin UI
Go to Zipkin with your browser [http://localhost:9411/]()

<p align="center"><img src="images/trace1.png?raw=true"/></p>

## Useful links
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
- For more information on OpenTelemetry for Node.js, visit: <https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-node>

## LICENSE

Apache License 2.0
39 changes: 39 additions & 0 deletions examples/fastify/client.js
@@ -0,0 +1,39 @@
'use strict';

// eslint-disable-next-line import/order
const tracing = require('./tracing')('example-fastify-client');

const { tracer } = tracing;
const api = require('@opentelemetry/api');
const axios = require('axios').default;

function makeRequest() {
tracing.log('starting');
const span = tracer.startSpan('client.makeRequest()', {
kind: api.SpanKind.CLIENT,
});

api.context.with(api.trace.setSpan(api.ROOT_CONTEXT, span), async () => {
try {
const res = await axios.post('http://localhost:8080/run_test/1', {
// testing
// const res = await axios.post('http://localhost:8080/run_test2/1', {
headers: {
'Content-Type': 'application/json',
},
timeout: 3000,
});
tracing.log('status:', res.statusText);
span.setStatus({ code: api.SpanStatusCode.OK });
} catch (e) {
tracing.log('failed:', e.message);
span.setStatus({ code: api.SpanStatusCode.ERROR, message: e.message });
}
span.end();
tracing.log('forcing spans to be exported');
await tracing.provider.shutdown();
tracing.log('all spans exported successfully.');
});
}

makeRequest();
28 changes: 28 additions & 0 deletions examples/fastify/docker/collector-config.yaml
@@ -0,0 +1,28 @@
receivers:
otlp:
protocols:
grpc:
http:
cors_allowed_origins:
- http://*
- https://*

exporters:
zipkin:
endpoint: "http://zipkin-all-in-one:9411/api/v2/spans"
prometheus:
endpoint: "0.0.0.0:9464"

processors:
batch:

service:
pipelines:
traces:
receivers: [otlp]
exporters: [zipkin]
processors: [batch]
metrics:
receivers: [otlp]
exporters: [prometheus]
processors: [batch]
21 changes: 21 additions & 0 deletions examples/fastify/docker/docker-compose.yaml
@@ -0,0 +1,21 @@
version: "3"
services:
# Collector
collector:
image: otel/opentelemetry-collector:0.30.0
# image: otel/opentelemetry-collector:latest
command: ["--config=/conf/collector-config.yaml", "--log-level=DEBUG"]
volumes:
- ./collector-config.yaml:/conf/collector-config.yaml
ports:
- "9464:9464"
- "4317:4317"
- "55681:55681"
depends_on:
- zipkin-all-in-one

# Zipkin
zipkin-all-in-one:
image: openzipkin/zipkin:latest
ports:
- "9411:9411"
Binary file added examples/fastify/images/trace1.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
59 changes: 59 additions & 0 deletions examples/fastify/package.json
@@ -0,0 +1,59 @@
{
"name": "example-fastify",
"private": true,
"version": "0.25.0",
"description": "Example of Fastify integration with OpenTelemetry",
"main": "index.js",
"scripts": {
"client": "node ./client.js",
"docker:start": "cd ./docker && docker-compose down && docker-compose up",
"docker:stop": "cd ./docker && docker-compose down",
"server": "node ./server.js"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/open-telemetry/opentelemetry-js.git"
},
"keywords": [
"opentelemetry",
"express",
"tracing"
],
"engines": {
"node": ">=8"
},
"files": [
"build/src/**/*.js",
"build/src/**/*.map",
"build/src/**/*.d.ts",
"doc",
"LICENSE",
"README.md"
],
"author": "OpenTelemetry Authors",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/open-telemetry/opentelemetry-js/issues"
},
"dependencies": {
"@opentelemetry/api": "^1.0.2",
"@opentelemetry/exporter-jaeger": "^1.0.0",
"@opentelemetry/exporter-zipkin": "^1.0.0",
"@opentelemetry/exporter-collector": "^0.25.0",
"@opentelemetry/instrumentation": "^0.26.0",
"@opentelemetry/instrumentation-fastify": "^0.25.0",
"@opentelemetry/instrumentation-http": "^0.26.0",
"@opentelemetry/sdk-trace-node": "^1.0.0",
"@opentelemetry/resources": "^1.0.0",
"@opentelemetry/semantic-conventions": "^1.0.0",
"@opentelemetry/sdk-trace-base": "^1.0.0",
"axios": "^0.21.1",
"cross-env": "^7.0.3",
"fastify": "^3.19.2",
"fastify-cors": "^6.0.2",
"fastify-express": "^0.3.3",
"middie": "^5.3.0"
},
"homepage": "https://github.com/open-telemetry/opentelemetry-js#readme",
"devDependencies": {}
}
76 changes: 76 additions & 0 deletions examples/fastify/server.js
@@ -0,0 +1,76 @@
'use strict';

// eslint-disable-next-line
const tracing = require('./tracing')('example-fastify-server');
const opentelemetry = require('@opentelemetry/api');

const { context, trace } = opentelemetry;
const Fastify = require('fastify');
const axios = require('axios');

const PORT = 8080;
const app = Fastify({ logger: true });
app
.register(require('fastify-express'))
.register(subsystem);

async function subsystem(fastify) {
fastify.addHook('onRequest', async () => {
const span = trace.getSpan(context.active());
span.setAttribute('order', 2);
});

// eslint-disable-next-line prefer-arrow-callback
fastify.addHook('onRequest', async function onRequestHook() {
const span = trace.getSpan(context.active());
span.setAttribute('order', 3);

const newSpan = tracing.tracer.startSpan('foo');
newSpan.setAttribute('foo', 'bar');
newSpan.end();
});

fastify.use((req, res, next) => {
const span = trace.getSpan(context.active());
span.setAttribute('order', 1);
next();
});

fastify.post('/run_test2/:id', async (req, res) => {
const span = trace.getSpan(context.active());
span.setAttribute('order', 4);

const result = await axios.get('https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/main/package.json');
const result2 = await axios.get('https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/main/package.json');

tracing.log('sending response');
// throw Error('boom lala');
res.send(`OK ${result.data.version} ${result2.data.version}`);
});

fastify.addHook('onRequest', (req, reply, done) => {
const span = trace.getSpan(context.active());
console.log('first', span);
console.log('kuku1');
span.setAttribute('kuku1', 'lala');

setTimeout(() => {
console.log('kuku2');
span.setAttribute('kuku2', 'lala');
const newSpan = tracing.tracer.startSpan('tada');
newSpan.end();

reply.send('foo');
done();
}, 2000);
});

}

app.post('/run_test/:id', async (req, res) => {
const result = await axios.get('https://raw.githubusercontent.com/open-telemetry/opentelemetry-js/main/package.json');
tracing.log('sending response');
res.send(`OK ${result.data.version}`);
});

app.listen(PORT);
52 changes: 52 additions & 0 deletions examples/fastify/tracing.js
@@ -0,0 +1,52 @@
'use strict';

const opentelemetry = require('@opentelemetry/api');

const { diag, DiagConsoleLogger, DiagLogLevel } = opentelemetry;
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);

const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { CollectorTraceExporter } = require('@opentelemetry/exporter-collector');

const { FastifyInstrumentation } = require('@opentelemetry/instrumentation-fastify');
const { HttpInstrumentation } = require('@opentelemetry/instrumentation-http');

function log() {
// eslint-disable-next-line prefer-rest-params
const args = Array.from(arguments) || [];
args.unshift(new Date());
console.log.apply(this, args);
}

module.exports = (serviceName) => {
const provider = new NodeTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
}),
});
const fastifyInstrumentation = new FastifyInstrumentation();
registerInstrumentations({
tracerProvider: provider,
instrumentations: [
// Fastify instrumentation expects HTTP layer to be instrumented
HttpInstrumentation,
fastifyInstrumentation,
],
});

const exporter = new CollectorTraceExporter();
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));

// Initialize the OpenTelemetry APIs to use the NodeTracerProvider bindings
provider.register({});
return {
log,
fastifyInstrumentation,
provider,
tracer: opentelemetry.trace.getTracer('fastify-example'),
};
};
1 change: 0 additions & 1 deletion examples/grpc_dynamic_codegen/tracer.js
Expand Up @@ -8,7 +8,6 @@ const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { GrpcInstrumentation } = require('@opentelemetry/instrumentation-grpc');


const EXPORTER = process.env.EXPORTER || '';

module.exports = (serviceName) => {
Expand Down
1 change: 1 addition & 0 deletions examples/mongodb/server.js
Expand Up @@ -68,6 +68,7 @@ function handleInsertQuery(response) {
} else {
console.log('1 document inserted');
// find document to test context propagation using callback
// eslint-disable-next-line prefer-arrow-callback
collection.findOne({}, function () {
response.end();
});
Expand Down
2 changes: 1 addition & 1 deletion examples/web/webpack.config.js
Expand Up @@ -10,7 +10,7 @@ const common = {
mode: 'development',
entry: {
'document-load': 'examples/document-load/index.js',
'meta': 'examples/meta/index.js',
meta: 'examples/meta/index.js',
'user-interaction': 'examples/user-interaction/index.js',
},
output: {
Expand Down
@@ -0,0 +1,2 @@
build
coverage

0 comments on commit 77c215b

Please sign in to comment.