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

feat(instrumentation-undici): add undici and fetch instrumentations #4393

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2d48adb
feat(instrumentation-undici): add new instrumentation package for und…
david-luna Dec 28, 2023
23c7890
tests(instrumentation-undici): add test file for fetch
david-luna Dec 28, 2023
be8c2a5
tests(instrumentation-undici): add attribute assertions
david-luna Dec 28, 2023
e091a8f
chore(instrumentation-undici): update readme
david-luna Dec 28, 2023
9870554
chore(instrumentation-undici): fix lint issues
david-luna Dec 28, 2023
c6b0258
chore(instrumentation-undici): fix types in tests
david-luna Dec 28, 2023
459eca8
chore(instrumentation-undici): fix types and skip tests
david-luna Dec 28, 2023
4a769eb
chore(instrumentation-undici): fix readme linting
david-luna Dec 28, 2023
092b9c1
chore(instrumentation-undici): add new span assertions
david-luna Jan 4, 2024
af72d4d
chore(instrumentation-undici): add instrumentation config
david-luna Jan 17, 2024
057206f
chore(instrumentation-undici): add tests for request/response headers…
david-luna Jan 22, 2024
6035a89
chore(instrumentation-undici): add createSpanHook handling
david-luna Jan 23, 2024
f0cce12
chore(instrumentation-undici): add disabled config test
david-luna Jan 23, 2024
f42885b
chore: generate new semconv for undici package
david-luna Jan 26, 2024
a616402
chore(instrumentation-undici): add new semconv in tests
david-luna Jan 26, 2024
c5ca3dc
chore(instrumentation-undici): update tests for headers
david-luna Jan 26, 2024
8ebdfc8
chore(instrumentation-undici): add tests for error capturing
david-luna Jan 26, 2024
bd61d75
chore(instrumentation-undici): add context propagation
david-luna Jan 28, 2024
62cdcc6
chore(instrumentation-undici): add test for requireParentSpan config
david-luna Feb 5, 2024
f9b40f2
chore(instrumentation-undici): add tests for undici request
david-luna Feb 5, 2024
9183108
chore(instrumentation-undici): add tests for request/fetch abort use …
david-luna Feb 5, 2024
e82dc40
chore(instrumentation-undici): add tests for fetch and stram methods
david-luna Feb 15, 2024
2e5f09a
chore(instrumentation-undici): add http.client.request.duration metric
david-luna Feb 19, 2024
032e057
chore(instrumentation-undici): add error.type attribute in request du…
david-luna Feb 19, 2024
0c0e169
chore(instrumentation-undici): clean up semantic attributes
david-luna Feb 19, 2024
7cd7487
chore(instrumentation-undici): fix lint issues
david-luna Feb 19, 2024
7d69f4a
chore(instrumentation-undici): fix compile error
david-luna Feb 20, 2024
db97899
chore(instrumentation-undici): add example
david-luna Feb 20, 2024
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
81 changes: 81 additions & 0 deletions examples/undici/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Overview

OpenTelemetry Undici Instrumentation allows the user to automatically collect trace data and export them to the backend of choice (we can use Zipkin or Jaeger for this example), to give observability to distributed systems.

This is a simple example that demonstrates tracing HTTP request from client to server. The example
shows key aspects of tracing such as

- Root Span (on Client)
- Child Span (on Client)
- Child Span from a Remote Parent (on Server)
- SpanContext Propagation (from Client to Server)
- Span Events
- Span Attributes

## Installation

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

Setup [Zipkin Tracing](https://zipkin.io/pages/quickstart.html)
or
Setup [Jaeger Tracing](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one)

## Run the Application

### Zipkin

- Run the server

```sh
# from this directory
npm run zipkin:server
```

- Run the client

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

#### Zipkin UI

`zipkin:server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`).
Go to Zipkin with your browser <http://localhost:9411/zipkin/traces/(your-trace-id)> (e.g <http://localhost:9411/zipkin/traces/4815c3d576d930189725f1f1d1bdfcc6>)

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

### Jaeger

- Run the server

```sh
# from this directory
npm run jaeger:server
```

- Run the client

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

#### Jaeger UI

`jaeger:server` script should output the `traceid` in the terminal (e.g `traceid: 4815c3d576d930189725f1f1d1bdfcc6`).
Go to Jaeger with your browser <http://localhost:16686/trace/(your-trace-id)> (e.g <http://localhost:16686/trace/4815c3d576d930189725f1f1d1bdfcc6>)

<p align="center"><img src="images/jaeger-ui.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-sdk-trace-node>

## LICENSE

Apache License 2.0
24 changes: 24 additions & 0 deletions examples/undici/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
'use strict';

const undici = require('undici');
const tracer = require('./tracer')('example-undici-client');

/** A function which makes requests and handles response. */
async function makeRequests(type) {
tracer.startActiveSpan('makeRequests with global fetch', async (span) => {
const fetchResponse = await fetch('localhost:8080/helloworld');
console.log('response with global fetch: ' + await fetchResponse.text());

const undiciResponse = await undici.fetch('localhost:8080/helloworld');
console.log('response with undici fetch: ' + await undiciResponse.text());
span.end();
});

// The process must live for at least the interval past any traces that
// must be exported, or some risk being lost if they are recorded after the
// last export.
console.log('Sleeping 5 seconds before shutdown to ensure all records are flushed.');
setTimeout(() => { console.log('Completed.'); }, 5000);
}

makeRequests();
21 changes: 21 additions & 0 deletions examples/undici/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
version: '3.7'

services:
jaeger:
image: jaegertracing/all-in-one
ports:
- "16686:16686"
- "4318:4318"
environment:
- LOG_LEVEL=debug
networks:
- undici-example
zipkin:
image: openzipkin/zipkin
container_name: zipkin
ports:
# Port used for the Zipkin UI and HTTP Api
- 9411:9411

networks:
undici-example:
48 changes: 48 additions & 0 deletions examples/undici/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"name": "undici-example",
"private": true,
"version": "0.46.0",
"description": "Example of Undici integration with OpenTelemetry",
"main": "index.js",
"scripts": {
"docker:start": "docker compose -f .docker-compose.yml up -d",
"docker:stop": "docker compose -f ./docker-compose.yml down",
"zipkin:server": "cross-env EXPORTER=zipkin node ./server.js",
"zipkin:client": "cross-env EXPORTER=zipkin node ./client.js",
"jaeger:server": "cross-env EXPORTER=jaeger node ./server.js",
"jaeger:client": "cross-env EXPORTER=jaeger node ./client.js"
},
"repository": {
"type": "git",
"url": "git+ssh://[email protected]/open-telemetry/opentelemetry-js.git"
},
"keywords": [
"opentelemetry",
"undici",
"fetch",
"tracing"
],
"engines": {
"node": ">=14"
},
"author": "OpenTelemetry Authors",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/open-telemetry/opentelemetry-js/issues"
},
"dependencies": {
"@opentelemetry/api": "^1.3.0",
"@opentelemetry/exporter-zipkin": "1.19.0",
"@opentelemetry/instrumentation": "0.46.0",
"@opentelemetry/instrumentation-undici": "0.46.0",
"@opentelemetry/exporter-trace-otlp-proto": "0.46.0",
"@opentelemetry/resources": "1.19.0",
"@opentelemetry/sdk-trace-base": "1.19.0",
"@opentelemetry/sdk-trace-node": "1.19.0",
"@opentelemetry/semantic-conventions": "1.19.0"
},
"homepage": "https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/undici",
"devDependencies": {
"cross-env": "^6.0.0"
}
}
45 changes: 45 additions & 0 deletions examples/undici/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
'use strict';

const api = require('@opentelemetry/api');
const tracer = require('./tracer')('example-undici-server');
const http = require('http');

/** Starts a HTTP server that receives requests on sample server port. */
function startServer(port) {
// Creates a server
const server = http.createServer(handleRequest);
// Starts the server
server.listen(port, (err) => {
if (err) {
throw err;
}
console.log(`Node HTTP listening on ${port}`);
});
}

/** A function which handles requests and send response. */
function handleRequest(request, response) {
const currentSpan = api.trace.getActiveSpan();
// display traceid in the terminal
const traceId = currentSpan.spanContext().traceId;
console.log(`traceId: ${traceId}`);
const span = tracer.startSpan('handleRequest', {
kind: 1, // server
attributes: { key: 'value' },
});
// Annotate our span to capture metadata about the operation
span.addEvent('invoking handleRequest');

const body = [];
request.on('error', (err) => console.log(err));
request.on('data', (chunk) => body.push(chunk));
request.on('end', () => {
// deliberately sleeping to mock some action.
setTimeout(() => {
span.end();
response.end('Hello World!');
}, 2000);
});
}

startServer(8080);
42 changes: 42 additions & 0 deletions examples/undici/tracer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use strict';

const opentelemetry = require('@opentelemetry/api');
const { registerInstrumentations } = require('@opentelemetry/instrumentation');
const { NodeTracerProvider } = require('@opentelemetry/sdk-trace-node');
const { Resource } = require('@opentelemetry/resources');
const { SemanticResourceAttributes } = require('@opentelemetry/semantic-conventions');
const { SimpleSpanProcessor } = require('@opentelemetry/sdk-trace-base');
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-proto');
const { ZipkinExporter } = require('@opentelemetry/exporter-zipkin');
const { UndiciInstrumentation } = require('@opentelemetry/instrumentation-undici');

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

module.exports = (serviceName) => {
const provider = new NodeTracerProvider({
resource: new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: serviceName,
}),
});

let exporter;
if (EXPORTER.toLowerCase().startsWith('z')) {
exporter = new ZipkinExporter();
} else {
exporter = new OTLPTraceExporter();
}

provider.addSpanProcessor(new SimpleSpanProcessor(exporter));

// Initialize the OpenTelemetry APIs to use the NodeTracerProvider bindings
provider.register();

registerInstrumentations({
// // when boostraping with lerna for testing purposes
instrumentations: [
new UndiciInstrumentation(),
],
});

return opentelemetry.trace.getTracer('undici-example');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports = {
"env": {
"mocha": true,
"node": true
},
...require('../../../eslint.base.js')
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/bin
/coverage
/doc
/test
Loading
Loading