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

Add w3c trace context propagation example #78

Merged
merged 15 commits into from
Mar 27, 2024
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ The repository includes example applications and configurations for Datadog user
| [Span Links][12] | Distributed app with Kafka messages, OTel Go and Java instrumentations | OTel span links |
| [W3C Trace Context][13] | Java and Python app to demonstrate W3C trace context propagation between OTel and DD instrumented apps | W3C trace context, runtime metrics |
| [Kubernetes (Datadog Operator and Helm) with Express][15] | An Express sample app configured with Kubernetes | Kubernetes |
| [Python and Javascript trace context propagation][17] | An Express controller server calling two Flask servers | Standalone Host |


[1]: https://opentelemetry.io/
Expand All @@ -35,7 +36,8 @@ The repository includes example applications and configurations for Datadog user
[10]: ./apps/rest-services/py/
[11]: ./apps/rpc/
[12]: ./apps/span-links/
[13]: ./apps/w3-trace-context/
[13]: ./apps/w3c-trace-context/
[14]: ./guides/common-mistakes.md
[15]: ./configurations/
[16]: ./apps/kubernetes-express-otel/
[16]: ./apps/kubernetes-express-otel/
[17]: ./apps/w3c-trace-context-ex2/
32 changes: 32 additions & 0 deletions apps/rolldice-game/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# W3C Trace Context Example
This project consists of two Flask servers and one Express server instrumented with Opentelemetry on a standalone host. (This is not using k8s or containers)

## Start the Demo
### Option 1 (Standalone Host)
There are a few required steps to get this example working.
1. Install and set up the OpenTelemetry Collector on the host of your choice. https://opentelemetry.io/docs/collector/installation/
2. Set up the Collector configuration. An example collector configuration can be found at [Config File](./config.yaml).
* The config.yaml is set up to send traces and metrics from OTLP Collector to the Datadog Exporter.
* Update the DATADOG_API_KEY as well.
3. Run this collector ~ `./otelcol-contrib --config=config.yaml`
4. Once the collector is up and running. Set up the 3 servers. (Can be found in each of the servers README)

### Option 2 (Docker)
1. Update DATADOG_API_KEY in [Config File](./config.yaml).
2. Run docker-compose up

To trigger the service call

### Success
```bash
curl -X POST http://localhost:5002/play_game \
-H "Content-Type: application/json" \
-d '{"player": "John Doe"}'
```
### Error
``` bash
curl -X POST http://localhost:5002/play_game \
-H "Content-Type: application/json" \
-d '{}'
```
View traces & standalone host in app.datadoghq.com
44 changes: 44 additions & 0 deletions apps/rolldice-game/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
hostmetrics:
# collect metrics every 10 seconds.
collection_interval: 10s
scrapers:
cpu:
disk:
filesystem:
memory:
network:
load:
paging:
processes:

exporters:
# NOTE: Prior to v0.86.0 use `logging` instead of `debug`.
debug:
verbosity: detailed
datadog/exporter:
api:
site: datadoghq.com
key: <DATADOG_API_KEY>

processors:
batch:
connectors:
datadog/connector:
service:
pipelines:
traces:
receivers: [otlp]
processors: [batch]
exporters: [datadog/connector, datadog/exporter]
metrics:
receivers: [datadog/connector, otlp]
processors: [batch]
exporters: [datadog/exporter]

66 changes: 66 additions & 0 deletions apps/rolldice-game/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
version: '3'
services:
game_controller:
build:
context: ./game_controller
dockerfile: Dockerfile
ports:
- "5002:5002"
depends_on:
otelcol:
condition: service_started
rolling:
condition: service_started
scoring:
condition: service_started
environment:
- OTEL_SERVICE_NAME=controller
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317


rolling:
build:
context: ./rolling
dockerfile: Dockerfile
ports:
- "5004:5004"
depends_on:
otelcol:
condition: service_started
environment:
- OTEL_SERVICE_NAME=rolly
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317


scoring:
build:
context: ./scoring
dockerfile: Dockerfile
ports:
- "5001:5001"
depends_on:
otelcol:
condition: service_started
environment:
- OTEL_SERVICE_NAME=scorey
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317


otelcol:
image: otel/opentelemetry-collector-contrib
deploy:
resources:
limits:
memory: 200M
restart: unless-stopped
command: [ "--config=/etc/otelcol-config.yml"]
volumes:
- ./config.yml:/etc/otelcol-config.yml
ports:
- "4318:4318" # OTLP http receiver
- "4317:4317"
environment:
- DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317
- DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT=0.0.0.0:4318


23 changes: 23 additions & 0 deletions apps/rolldice-game/game_controller/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Use an official Node.js runtime as the base image
FROM node:14-alpine

# Set the working directory
WORKDIR /game_controller

# Copy the project files to the working directory
COPY . ./game_controller
COPY package*.json ./game_controller/

WORKDIR /game_controller

# Install the project dependencies
RUN npm install
ENV service_name=controller
ENV logs_exporter=otlp


# Expose port 5002 for the app to be accessible
EXPOSE 5002

# Define the command to run the app
CMD [ "node", "game_controller/controller.js" ]
19 changes: 19 additions & 0 deletions apps/rolldice-game/game_controller/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Game Controller
This project is a simple Express.js application that uses the OpenTelemetry API for tracing and axios for HTTP requests.


## Installation
* Node.js (v12 or later)
* npm (v6 or later)


```bash
npm install
```

## Run the server

```bash
opentelemetry-instrument --service_name controller --logs_exporter otlp node controller.js
```

47 changes: 47 additions & 0 deletions apps/rolldice-game/game_controller/controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const express = require('express');
const axios = require('axios');
const { context, trace, propagation } = require('@opentelemetry/api');
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');

const sdk = new NodeSDK({
instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();
const app = express();
app.use(express.json());

app.post('/play_game', async (req, res) => {
const tracer = trace.getTracer('express-game-controller');
const span = tracer.startSpan('play_game');

const ctx = trace.setSpan(context.active(), span);

context.with(ctx, async () => {
try {
const player = req.body.player;
const headers = {};
propagation.inject(context.active(), headers);
const diceRollResult = await axios.get(`http://rolling:5004/rolldice?player=${player}`, { headers });
const updateScoreResult = await axios.post('http://scoring:5001/update_score', {
player: player,
result: diceRollResult.data
}, { headers });

span.addEvent('score_updated');
span.end();
res.json(updateScoreResult.data);
} catch (error) {
span.recordException(error);
span.end();
console.error(error);
res.status(500).send('An error occurred');
}
});
});

const PORT = process.env.PORT || 5002;
app.listen(PORT, () => {
console.log(`Game Controller service listening at http://localhost:${PORT}`);
});
Loading
Loading