Skip to content
Draft
Show file tree
Hide file tree
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
7 changes: 7 additions & 0 deletions apps/rolldice-game/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Required: Your Datadog API key for the OTel Collector Datadog exporter.
# Get your API key from https://app.datadoghq.com/organization-settings/api-keys
DD_API_KEY=your_datadog_api_key_here

# Optional: Your Datadog site. Defaults to datadoghq.com.
# Other options: datadoghq.eu, us3.datadoghq.com, us5.datadoghq.com, ap1.datadoghq.com
DD_SITE=datadoghq.com
111 changes: 81 additions & 30 deletions apps/rolldice-game/README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,83 @@
# 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.yml).
* The config.yml 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.yml`
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.yml).
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"}'
# Rolldice Game - W3C Trace Context Example

This project demonstrates **distributed tracing with W3C Trace Context propagation** across three microservices instrumented with OpenTelemetry, exporting to Datadog via the OTel Collector.

## Architecture

```
### Error
``` bash
curl -X POST http://localhost:5002/play_game \
-H "Content-Type: application/json" \
-d '{}'
Client --> game_controller (Node.js/Express) --> rolling (Python/Flask)
--> scoring (Python/Flask)
```
View traces & standalone host in app.datadoghq.com

- **game_controller**: Node.js Express service that orchestrates the game. Calls the rolling and scoring services.
- **rolling**: Python Flask service that simulates a dice roll and tracks roll metrics.
- **scoring**: Python Flask service that tracks player scores.

All services export traces, metrics, and logs via OTLP to an OpenTelemetry Collector, which forwards them to Datadog.

## Prerequisites

- Docker and Docker Compose
- A [Datadog API key](https://app.datadoghq.com/organization-settings/api-keys)

## Quick Start (Docker)

1. Copy the environment file and set your Datadog API key:
```bash
cp .env.example .env
# Edit .env and set DD_API_KEY
```

2. Start all services:
```bash
docker compose up --build
```

3. Trigger the service call:

**Success:**
```bash
curl -X POST http://localhost:5002/play_game \
-H "Content-Type: application/json" \
-d '{"player": "John Doe"}'
```

**Error (missing player):**
```bash
curl -X POST http://localhost:5002/play_game \
-H "Content-Type: application/json" \
-d '{}'
```

4. View traces and metrics in [Datadog APM](https://app.datadoghq.com/apm/traces).

## Standalone Host Setup

1. Install and configure the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/installation/) on your host.
2. Use the provided [config.yml](./config.yml) as your collector configuration. Set `DD_API_KEY` in your environment.
3. Start the collector: `./otelcol-contrib --config=config.yml`
4. Start each service (see individual service READMEs for instructions).

## Trace Context Propagation

This example demonstrates W3C TraceContext propagation between services:

1. The **game_controller** receives a request and starts a trace.
2. When calling the **rolling** service, W3C `traceparent` headers are automatically injected by the OTel HTTP instrumentation.
3. The **rolling** service extracts the trace context from incoming headers, creating child spans under the same trace.
4. The same propagation happens when the **game_controller** calls the **scoring** service.

This results in a single distributed trace visible in Datadog APM that spans all three services.

## Configuration

All OTel configuration is done via environment variables (see [docker-compose.yml](./docker-compose.yml)):

| Variable | Description |
|----------|-------------|
| `OTEL_SERVICE_NAME` | Sets the service name in Datadog APM |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTel Collector endpoint |
| `OTEL_EXPORTER_OTLP_PROTOCOL` | Transport protocol (grpc) |
| `OTEL_PROPAGATORS` | Context propagation format (tracecontext,baggage) |
| `DD_API_KEY` | Datadog API key for the collector |
| `DD_SITE` | Datadog site (default: datadoghq.com) |
60 changes: 45 additions & 15 deletions apps/rolldice-game/config.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
# OpenTelemetry Collector configuration for Datadog export.
# This config follows the recommended patterns from:
# https://docs.datadoghq.com/opentelemetry/collector_exporter/
# https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/datadogexporter/examples/collector.yaml
#
# Required environment variable:
# DD_API_KEY - Your Datadog API key
# DD_SITE - (optional) Your Datadog site (default: datadoghq.com)

receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318

# Collect host-level infrastructure metrics every 10 seconds.
hostmetrics:
# collect metrics every 10 seconds.
collection_interval: 10s
scrapers:
cpu:
Expand All @@ -18,31 +28,51 @@ receivers:
paging:
processes:

processors:
# Batch telemetry data before export to reduce overhead.
# The 10s timeout is recommended by Datadog for trace pipelines.
batch:
timeout: 10s

# The Datadog Connector computes APM trace metrics (hit counts, error rates,
# latency distributions) from trace data. Since OTel Collector v0.95.0+,
# this is the recommended way to generate APM stats (instead of the exporter).
connectors:
datadog/connector:

exporters:
# NOTE: Prior to v0.86.0 use `logging` instead of `debug`.
# Debug exporter for local troubleshooting (set verbosity to "basic" in production).
debug:
verbosity: detailed

# Datadog exporter sends traces, metrics, and logs to Datadog.
# DD_API_KEY must be set in the environment.
# DD_SITE defaults to datadoghq.com; override for other Datadog sites
# (e.g., datadoghq.eu, us3.datadoghq.com, us5.datadoghq.com).
datadog/exporter:
api:
site: datadoghq.com
site: ${env:DD_SITE:-datadoghq.com}
key: ${env:DD_API_KEY}

processors:
batch:
connectors:
datadog/connector:

service:
pipelines:
traces/sampling:
receivers: [datadog/connector]
processors: [] # Add any sampling here
exporters: [datadog/exporter]
# Primary traces pipeline: receives OTLP traces, batches them, and sends to
# both the Datadog connector (for APM stats) and the Datadog exporter (for traces).
traces:
receivers: [otlp]
processors: [batch]
exporters: [datadog/connector]
exporters: [datadog/connector, datadog/exporter]

# Metrics pipeline: receives APM stats from the connector plus any OTLP metrics,
# batches them, and exports to Datadog.
metrics:
receivers: [datadog/connector, otlp]
receivers: [datadog/connector, hostmetrics, otlp]
processors: [batch]
exporters: [datadog/exporter]

# Logs pipeline: receives OTLP logs and exports to Datadog.
# This enables log-trace correlation when services send logs via OTLP.
logs:
receivers: [otlp]
processors: [batch]
exporters: [datadog/exporter]

66 changes: 48 additions & 18 deletions apps/rolldice-game/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@ services:
otelcol:
condition: service_started
rolling:
condition: service_started
condition: service_healthy
scoring:
condition: service_started
condition: service_healthy
environment:
- OTEL_SERVICE_NAME=controller
# OTEL_SERVICE_NAME sets the service.name resource attribute, which maps
# directly to the Datadog APM service name.
- OTEL_SERVICE_NAME=game-controller
# OTEL_EXPORTER_OTLP_ENDPOINT configures where the SDK sends traces/metrics.
# This points to the OTel Collector running as a sidecar in this compose setup.
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
- OTEL_EXPORTER_OTLP_PROTOCOL=grpc

# W3C TraceContext is the default propagator; set explicitly for clarity.
- OTEL_PROPAGATORS=tracecontext,baggage
# Enable log auto-instrumentation for log-trace correlation in Datadog.
- OTEL_LOG_LEVEL=info
deploy:
resources:
limits:
memory: 256M
cpus: '0.5'

rolling:
build:
Expand All @@ -29,10 +41,18 @@ services:
otelcol:
condition: service_started
environment:
- OTEL_SERVICE_NAME=rolly
- OTEL_SERVICE_NAME=rolling
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
- OTEL_EXPORTER_OTLP_PROTOCOL=grpc

- OTEL_PROPAGATORS=tracecontext,baggage
# Enable Python logging auto-instrumentation to inject trace context into logs.
- OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
- OTEL_PYTHON_LOG_CORRELATION=true
deploy:
resources:
limits:
memory: 256M
cpus: '0.5'

scoring:
build:
Expand All @@ -44,25 +64,35 @@ services:
otelcol:
condition: service_started
environment:
- OTEL_SERVICE_NAME=scorey
- OTEL_SERVICE_NAME=scoring
- OTEL_EXPORTER_OTLP_ENDPOINT=http://otelcol:4317
- OTEL_EXPORTER_OTLP_PROTOCOL=grpc

otelcol:
image: otel/opentelemetry-collector-contrib
- OTEL_PROPAGATORS=tracecontext,baggage
- OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
- OTEL_PYTHON_LOG_CORRELATION=true
deploy:
resources:
limits:
memory: 200M
memory: 256M
cpus: '0.5'

otelcol:
image: otel/opentelemetry-collector-contrib:latest
restart: unless-stopped
command: [ "--config=/etc/otelcol-config.yml"]
command: ["--config=/etc/otelcol-config.yml"]
volumes:
- ./config.yml:/etc/otelcol-config.yml
ports:
- "4318:4318" # OTLP http receiver
- "4317:4317"
- "4317:4317" # OTLP gRPC receiver
- "4318:4318" # OTLP HTTP receiver
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


# DD_API_KEY is required for the Datadog exporter.
# Set it in a .env file or export it before running docker compose.
- DD_API_KEY=${DD_API_KEY}
# DD_SITE defaults to datadoghq.com; override for EU or other sites.
- DD_SITE=${DD_SITE:-datadoghq.com}
deploy:
resources:
limits:
memory: 256M
cpus: '0.5'
32 changes: 16 additions & 16 deletions apps/rolldice-game/game_controller/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# Use an official Node.js runtime as the base image
FROM node:14-alpine
FROM node:20-alpine

# Set the working directory
WORKDIR /game_controller
WORKDIR /app

# Copy the project files to the working directory
COPY . ./game_controller
COPY package*.json ./game_controller/
# Copy dependency manifests first for better Docker layer caching
COPY package*.json ./

WORKDIR /game_controller
# Install production dependencies only
RUN npm install --omit=dev

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

# Copy application source
COPY controller.js ./

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

# Define the command to run the app
CMD [ "node", "game_controller/controller.js" ]
# Health check to verify the service is responding
HEALTHCHECK --interval=15s --timeout=5s --start-period=10s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:5002/health || exit 1

# Run as non-root user for security
USER node

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

This is an Express.js application that orchestrates the rolldice game. It uses the OpenTelemetry Node.js SDK for auto-instrumentation with W3C TraceContext propagation.

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

* Node.js (v20 or later)
* npm (v9 or later)

## Installation

```bash
npm install
```

## Run the server
## Run the server (standalone)

Set the required environment variables and start the server:

```bash
opentelemetry-instrument --service_name controller --logs_exporter otlp node controller.js
export OTEL_SERVICE_NAME=game-controller
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
export OTEL_EXPORTER_OTLP_PROTOCOL=grpc
node controller.js
```

The OTel SDK is initialized programmatically in `controller.js` before Express loads, which ensures all HTTP requests are automatically instrumented.
Loading