|
| 1 | +# Distributed Tracing with Azure Managed Durable Task Scheduler |
| 2 | + |
| 3 | +This example demonstrates **OpenTelemetry distributed tracing** with the Durable Task JavaScript SDK and Azure Managed Durable Task Scheduler (DTS). Traces are exported to [Jaeger](https://www.jaegertracing.io/) so you can visualize the full orchestration lifecycle as connected spans. |
| 4 | + |
| 5 | +## What You'll See |
| 6 | + |
| 7 | +When you run this example, the SDK automatically produces [W3C Trace Context](https://www.w3.org/TR/trace-context/) spans for every stage of a durable orchestration: |
| 8 | + |
| 9 | +| Span | Kind | Description | |
| 10 | +|------|------|-------------| |
| 11 | +| `create_orchestration:<name>` | PRODUCER | Client scheduling a new orchestration | |
| 12 | +| `orchestration:<name>` | SERVER | Worker executing the orchestration | |
| 13 | +| `activity:<name>` (scheduling) | CLIENT | Orchestrator scheduling an activity | |
| 14 | +| `activity:<name>` (execution) | SERVER | Worker executing the activity | |
| 15 | +| `timer:<orchestrationName>` | INTERNAL | Timer created inside an orchestration | |
| 16 | +| `orchestration_event:<eventName>` | PRODUCER | Event raised to another orchestration | |
| 17 | + |
| 18 | +All spans are linked via `traceparent` propagation, giving you a single end-to-end trace from the client all the way through parallel activity fan-out and back. |
| 19 | + |
| 20 | +### Sample Trace in Jaeger |
| 21 | + |
| 22 | +``` |
| 23 | +create_orchestration:dataPipelineOrchestrator (PRODUCER) |
| 24 | + └─ orchestration:dataPipelineOrchestrator (SERVER) |
| 25 | + ├─ activity:getDataSources (CLIENT → SERVER) |
| 26 | + ├─ activity:fetchData ×4 (CLIENT → SERVER, parallel) |
| 27 | + ├─ activity:transformData ×4 (CLIENT → SERVER, parallel) |
| 28 | + └─ activity:saveResults (CLIENT → SERVER) |
| 29 | +``` |
| 30 | + |
| 31 | +--- |
| 32 | + |
| 33 | +## Prerequisites |
| 34 | + |
| 35 | +- **Node.js ≥ 22** (required by the monorepo) |
| 36 | +- **Docker** (for the DTS Emulator and Jaeger) |
| 37 | +- **npm** (for installing dependencies) |
| 38 | + |
| 39 | +--- |
| 40 | + |
| 41 | +## Quick Start (Local with DTS Emulator) |
| 42 | + |
| 43 | +### 1. Start the DTS Emulator and Jaeger |
| 44 | + |
| 45 | +A `docker-compose.yml` is provided that starts both services: |
| 46 | + |
| 47 | +```bash |
| 48 | +cd examples/azure-managed |
| 49 | +docker compose up -d |
| 50 | +``` |
| 51 | + |
| 52 | +This starts: |
| 53 | + |
| 54 | +| Service | Port | Purpose | |
| 55 | +|---------|------|---------| |
| 56 | +| **DTS Emulator** | `8080` | Local gRPC endpoint (no authentication) | |
| 57 | +| **Jaeger UI** | `16686` | Trace visualization dashboard | |
| 58 | +| **Jaeger OTLP (HTTP)** | `4318` | OpenTelemetry trace receiver | |
| 59 | +| **Jaeger OTLP (gRPC)** | `4317` | OpenTelemetry trace receiver (gRPC) | |
| 60 | + |
| 61 | +> **Tip:** You can also run just the DTS Emulator standalone: |
| 62 | +> ```bash |
| 63 | +> docker run -d --name dts-emulator -p 8080:8080 -p 8082:8082 \ |
| 64 | +> mcr.microsoft.com/dts/dts-emulator:latest |
| 65 | +> ``` |
| 66 | +
|
| 67 | +### 2. Configure Environment Variables |
| 68 | +
|
| 69 | +Copy the provided emulator configuration: |
| 70 | +
|
| 71 | +```bash |
| 72 | +cp .env.emulator .env |
| 73 | +``` |
| 74 | +
|
| 75 | +This sets: |
| 76 | + |
| 77 | +```env |
| 78 | +DURABLE_TASK_SCHEDULER_CONNECTION_STRING=Endpoint=http://localhost:8080;Authentication=None;TaskHub=default |
| 79 | +OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 |
| 80 | +``` |
| 81 | + |
| 82 | +### 3. Install OpenTelemetry Dependencies |
| 83 | + |
| 84 | +From the **repository root**, install the required OTel packages: |
| 85 | + |
| 86 | +```bash |
| 87 | +npm install --no-save \ |
| 88 | + @opentelemetry/api \ |
| 89 | + @opentelemetry/sdk-node \ |
| 90 | + @opentelemetry/sdk-trace-base \ |
| 91 | + @opentelemetry/exporter-trace-otlp-http \ |
| 92 | + @opentelemetry/resources \ |
| 93 | + @opentelemetry/semantic-conventions |
| 94 | +``` |
| 95 | + |
| 96 | +> **Note:** `@opentelemetry/api` is an optional peer dependency of `@microsoft/durabletask-js`. When it's installed, the SDK automatically produces distributed tracing spans — no code changes needed in your orchestrations or activities. |
| 97 | +
|
| 98 | +### 4. Build the SDK (if not already built) |
| 99 | + |
| 100 | +```bash |
| 101 | +npm run build |
| 102 | +``` |
| 103 | + |
| 104 | +### 5. Run the Example |
| 105 | + |
| 106 | +```bash |
| 107 | +npm run example -- ./examples/azure-managed/distributed-tracing.ts |
| 108 | +``` |
| 109 | + |
| 110 | +You should see output like: |
| 111 | + |
| 112 | +``` |
| 113 | +OpenTelemetry SDK started – exporting traces to http://localhost:4318 |
| 114 | +
|
| 115 | +=== Sequence Orchestration === |
| 116 | +Scheduled: abc123 |
| 117 | +Completed – result: ["Hello, Tokyo!","Hello, Seattle!","Hello, London!"] |
| 118 | +
|
| 119 | +=== Data Pipeline Orchestration === |
| 120 | +Scheduled: def456 |
| 121 | +Completed – result: {"sourcesProcessed":4,"resultsSaved":4,"data":["transformed(data-from-users-api)",...]} |
| 122 | +
|
| 123 | +=== All orchestrations completed! === |
| 124 | +Open Jaeger UI at http://localhost:16686 and search for service "durabletask-js-tracing-example" to view traces. |
| 125 | +``` |
| 126 | + |
| 127 | +### 6. View Traces in Jaeger |
| 128 | + |
| 129 | +1. Open [http://localhost:16686](http://localhost:16686) in your browser. |
| 130 | +2. Select the service **`durabletask-js-tracing-example`** from the dropdown. |
| 131 | +3. Click **Find Traces**. |
| 132 | +4. Click on a trace to explore the span waterfall. |
| 133 | + |
| 134 | +--- |
| 135 | + |
| 136 | +## Running Against Azure Managed DTS (Cloud) |
| 137 | + |
| 138 | +To run this example against a real Azure Managed Durable Task Scheduler endpoint instead of the local emulator: |
| 139 | + |
| 140 | +### 1. Create a `.env` file |
| 141 | + |
| 142 | +```env |
| 143 | +# Option A: Connection string |
| 144 | +DURABLE_TASK_SCHEDULER_CONNECTION_STRING=Endpoint=https://your-scheduler.eastus.durabletask.io;Authentication=DefaultAzure;TaskHub=your-taskhub |
| 145 | +
|
| 146 | +# Option B: Explicit parameters (uses DefaultAzureCredential) |
| 147 | +# AZURE_DTS_ENDPOINT=https://your-scheduler.eastus.durabletask.io |
| 148 | +# AZURE_DTS_TASKHUB=your-taskhub |
| 149 | +
|
| 150 | +# OTLP endpoint (Jaeger, Azure Monitor, Aspire Dashboard, etc.) |
| 151 | +OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 |
| 152 | +``` |
| 153 | + |
| 154 | +### 2. Authenticate |
| 155 | + |
| 156 | +Make sure you're logged in via Azure CLI: |
| 157 | + |
| 158 | +```bash |
| 159 | +az login |
| 160 | +``` |
| 161 | + |
| 162 | +### 3. Run |
| 163 | + |
| 164 | +```bash |
| 165 | +npm run example -- ./examples/azure-managed/distributed-tracing.ts |
| 166 | +``` |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +## Using Azure Monitor / Application Insights |
| 171 | + |
| 172 | +To export traces to **Azure Monitor** instead of Jaeger, replace the OTLP exporter with the Azure Monitor exporter: |
| 173 | + |
| 174 | +```bash |
| 175 | +npm install --no-save @azure/monitor-opentelemetry-exporter |
| 176 | +``` |
| 177 | + |
| 178 | +Then modify the OpenTelemetry setup in `distributed-tracing.ts`: |
| 179 | + |
| 180 | +```typescript |
| 181 | +import { AzureMonitorTraceExporter } from "@azure/monitor-opentelemetry-exporter"; |
| 182 | + |
| 183 | +const traceExporter = new AzureMonitorTraceExporter({ |
| 184 | + connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING, |
| 185 | +}); |
| 186 | +``` |
| 187 | + |
| 188 | +--- |
| 189 | + |
| 190 | +## DTS Emulator Docker Image Reference |
| 191 | + |
| 192 | +| Property | Value | |
| 193 | +|----------|-------| |
| 194 | +| **Image** | `should be mcr.microsoft.com/dts/dts-emulator:latest` | |
| 195 | +| **gRPC Port** | `8080` | |
| 196 | +| **Dashboard Port** | `8082` | |
| 197 | +| **Authentication** | `None` (no credentials required) | |
| 198 | +| **Connection String** | `Endpoint=http://localhost:8080;Authentication=None;TaskHub=default` | |
| 199 | + |
| 200 | +### Running the Emulator Standalone |
| 201 | + |
| 202 | +```bash |
| 203 | +docker run -d \ |
| 204 | + --name dts-emulator \ |
| 205 | + -p 8080:8080 \ |
| 206 | + -p 8082:8082 \ |
| 207 | + should be mcr.microsoft.com/dts/dts-emulator:latest |
| 208 | +``` |
| 209 | + |
| 210 | +### Stopping the Emulator |
| 211 | + |
| 212 | +```bash |
| 213 | +docker stop dts-emulator && docker rm dts-emulator |
| 214 | +``` |
| 215 | + |
| 216 | +--- |
| 217 | + |
| 218 | +## How Distributed Tracing Works |
| 219 | + |
| 220 | +The Durable Task JavaScript SDK uses OpenTelemetry as an **optional peer dependency**. When `@opentelemetry/api` is installed and a `TracerProvider` is registered, the SDK automatically: |
| 221 | + |
| 222 | +1. **Creates spans** for orchestration scheduling, execution, activity scheduling/execution, timers, and events. |
| 223 | +2. **Propagates W3C `traceparent`** through the gRPC protocol so that spans from the client, orchestrator, and activities are all linked in a single trace. |
| 224 | +3. **Handles replay correctly** – on orchestration replay, the SDK carries forward the original span ID so all replay iterations correlate to the same logical orchestration execution. |
| 225 | + |
| 226 | +The tracer is registered under the name **`Microsoft.DurableTask`**, and spans include semantic attributes like: |
| 227 | + |
| 228 | +- `durabletask.type` – `orchestration`, `activity`, `timer`, `event`, etc. |
| 229 | +- `durabletask.task.name` – The function name. |
| 230 | +- `durabletask.task.instance_id` – The orchestration instance ID. |
| 231 | +- `durabletask.task.task_id` – The sequential task ID within an orchestration. |
| 232 | + |
| 233 | +--- |
| 234 | + |
| 235 | +## Cleanup |
| 236 | + |
| 237 | +```bash |
| 238 | +cd examples/azure-managed |
| 239 | +docker compose down |
| 240 | +``` |
| 241 | + |
| 242 | +--- |
| 243 | + |
| 244 | +## Files in This Example |
| 245 | + |
| 246 | +| File | Description | |
| 247 | +|------|-------------| |
| 248 | +| `distributed-tracing.ts` | Main example – OTel setup + orchestrations | |
| 249 | +| `docker-compose.yml` | DTS Emulator + Jaeger stack | |
| 250 | +| `.env.emulator` | Pre-configured env vars for the local emulator | |
| 251 | +| `.env.example` | Template for Azure Managed DTS (cloud) | |
| 252 | +| `index.ts` | Basic example (no tracing) | |
0 commit comments