From ff14d1f1c0c0ced1288deda11e11ed0f12f6d07c Mon Sep 17 00:00:00 2001 From: Donald Pinckney Date: Fri, 5 Jun 2026 12:01:45 -0400 Subject: [PATCH] Add TypeScript OpenTelemetry integration docs Split out from the OpenTelemetry plugins topic (PR #243) so the TypeScript material can be finalized separately. Adds the TS OTel integration reference, the Distributed Tracing section in TS observability, and the TS row in the integrations catalog. Co-Authored-By: Claude Opus 4.8 (1M context) --- references/integrations.md | 1 + .../typescript/integrations/opentelemetry.md | 77 +++++++++++++++++++ references/typescript/observability.md | 52 ++++++++++++- 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 references/typescript/integrations/opentelemetry.md diff --git a/references/integrations.md b/references/integrations.md index 71b3169..ee72d7c 100644 --- a/references/integrations.md +++ b/references/integrations.md @@ -19,3 +19,4 @@ Temporal ships and supports a growing set of integrations with third-party frame | LangSmith tracing (`temporalio.contrib.langsmith`) | Python | Experimental Temporal Plugin that propagates LangSmith trace context across Worker boundaries; lets `@traceable` run inside Workflows and Activities | `references/python/integrations/langsmith.md` | `references/python/ai-patterns.md`, `references/core/ai-patterns.md` | | LangGraph (`temporalio.contrib.langgraph`, Pre-release) | Python | Runs LangGraph Graph-API and Functional-API code as Temporal Workflows - nodes/tasks can execute as either in-workflow or as Activities | `references/python/integrations/langgraph.md` | `references/python/ai-patterns.md`, `references/core/ai-patterns.md` | | Google ADK (`temporalio[google-adk]`) | Python | Durable Google ADK agents: model calls run through `TemporalModel`-wrapped Activities, tools via `activity_tool`, MCP toolsets via `TemporalMcpToolSet` | `references/python/integrations/google-adk.md` | `references/python/ai-patterns.md`, `references/core/ai-patterns.md` | +| OpenTelemetry (`@temporalio/interceptors-opentelemetry`) | TypeScript | Distributed tracing for Temporal apps with OpenTelemetry | `references/typescript/integrations/opentelemetry.md` | `references/typescript/observability.md`, `references/typescript/standalone-activities.md` | diff --git a/references/typescript/integrations/opentelemetry.md b/references/typescript/integrations/opentelemetry.md new file mode 100644 index 0000000..3b54925 --- /dev/null +++ b/references/typescript/integrations/opentelemetry.md @@ -0,0 +1,77 @@ +# Temporal OpenTelemetry Integration (TypeScript) + +## Overview + +`@temporalio/interceptors-opentelemetry` wires OpenTelemetry tracing into Temporal through the `OpenTelemetryPlugin`. It traces Workflow Executions, Child Workflows, Activity invocations, and Client `start`/`signal` calls, propagating W3C TraceContext + Baggage across all of them. + +Workflow-side spans are emitted out of the Workflow isolate through an injected Sink that hands serialized spans to a host-side `SpanProcessor`. + +For non-OTel observability (metrics, runtime logger, sinks) read `references/typescript/observability.md`. + +> [!NOTE] +> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview. + +## Install the plugin + +Install `@temporalio/interceptors-opentelemetry` plus the OpenTelemetry peer packages you use — typically `@opentelemetry/api`, `@opentelemetry/sdk-trace-base`, and `@opentelemetry/resources` (plus an exporter package such as `@opentelemetry/exporter-trace-otlp-grpc` when you ship spans to a collector). + +## `OpenTelemetryPlugin` + +Construct one `OpenTelemetryPlugin` and pass it to the Client, `bundleWorkflowCode`, and `Worker.create`. It must reach `bundleWorkflowCode` so the Workflow-side interceptors are included in the bundle. Lifecycle spans (workflow / activity / client) are then created automatically. + +```ts +import { Resource } from '@opentelemetry/resources'; +import { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { NativeConnection, Worker, bundleWorkflowCode } from '@temporalio/worker'; +import { OpenTelemetryPlugin } from '@temporalio/interceptors-opentelemetry'; + +const resource = new Resource({ 'service.name': 'orders-worker' }); +const spanProcessor = new SimpleSpanProcessor(new ConsoleSpanExporter()); // swap in your own exporter + +const provider = new BasicTracerProvider({ resource }); +provider.addSpanProcessor(spanProcessor); +provider.register(); + +// `resource` and `spanProcessor` are required; pass an optional `tracer` to override +// the tracer used by the Client/Activity interceptors. +const plugin = new OpenTelemetryPlugin({ resource, spanProcessor }); + +const bundle = await bundleWorkflowCode({ + workflowsPath: require.resolve('./workflows'), + plugins: [plugin], +}); + +const connection = await NativeConnection.connect(); +const worker = await Worker.create({ + connection, + taskQueue: 'orders', + workflowBundle: bundle, + activities: { /* ... */ }, + plugins: [plugin], +}); +await worker.run(); +``` + +Pass the same plugin to the Client so client-side `start` and `signal` calls are traced: + +```ts +import { Client, Connection } from '@temporalio/client'; + +const client = new Client({ + connection: await Connection.connect(), + plugins: [plugin], +}); +``` + +The SDK uses the global OpenTelemetry propagator (default: W3C TraceContext + Baggage). To use a non-default propagator (e.g. Jaeger), call `propagation.setGlobalPropagator(...)` at the top level of your Workflow code BEFORE the Worker bundles it. + +## Common mistakes + +- **Passing only `resource` or only `spanProcessor`.** Both are required; `new OpenTelemetryPlugin()` with no argument throws. +- **Passing the plugin to `Worker.create` but not `bundleWorkflowCode`.** Workflow-side interceptors must be in the bundle. +- **Installing `@temporalio/opentelemetry`.** The package is `@temporalio/interceptors-opentelemetry`. +- **Expecting a non-default propagator (e.g. Jaeger) to work without setting the global propagator before `bundleWorkflowCode` runs.** + +## Resources + +- SDK metrics / observability reference: `references/typescript/observability.md` diff --git a/references/typescript/observability.md b/references/typescript/observability.md index 211fbc6..7f86642 100644 --- a/references/typescript/observability.md +++ b/references/typescript/observability.md @@ -2,7 +2,9 @@ ## Overview -The TypeScript SDK provides replay-aware logging, metrics, and integrations for production observability. +The TypeScript SDK provides replay-aware logging, metrics, and distributed tracing (OpenTelemetry) for production observability. + +These pillars are complementary: **logging** (below) captures discrete events, **metrics** capture aggregate worker health, **tracing** stitches a single request across Client/Workflow/Activity boundaries, and **Search Attributes** make executions queryable. ## Replay-Aware Logging @@ -100,6 +102,53 @@ Runtime.install({ }); ``` +## Distributed Tracing (OpenTelemetry) + +> [!NOTE] +> This feature is in Public Preview. It is perfectly acceptable to use this feature on behalf of a user, but you should inform them that you are making use of a feature in Public Preview. + +OpenTelemetry is the supported way to add distributed tracing to Temporal applications. The `OpenTelemetryPlugin` (from `@temporalio/interceptors-opentelemetry`) traces Workflow Executions, Child Workflows, Activity invocations, and Client `start`/`signal` calls, propagating W3C TraceContext + Baggage across all of them. Workflow-side spans are exported out of the Workflow isolate through an injected Sink. + +Construct one plugin instance and pass it to the Client, to `bundleWorkflowCode`, and to `Worker.create` (the Workflow-side interceptors must be in the bundle): + +```typescript +import { Resource } from '@opentelemetry/resources'; +import { BasicTracerProvider, ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'; +import { Client, Connection } from '@temporalio/client'; +import { NativeConnection, Worker, bundleWorkflowCode } from '@temporalio/worker'; +import { OpenTelemetryPlugin } from '@temporalio/interceptors-opentelemetry'; + +const resource = new Resource({ 'service.name': 'my-worker' }); +const spanProcessor = new SimpleSpanProcessor(new ConsoleSpanExporter()); // swap in your own exporter + +const provider = new BasicTracerProvider({ resource }); +provider.addSpanProcessor(spanProcessor); +provider.register(); + +const plugin = new OpenTelemetryPlugin({ resource, spanProcessor }); + +// In your worker process: +const bundle = await bundleWorkflowCode({ + workflowsPath: require.resolve('./workflows'), + plugins: [plugin], +}); +const worker = await Worker.create({ + connection: await NativeConnection.connect(), + taskQueue: 'my-task-queue', + workflowBundle: bundle, + plugins: [plugin], +}); +await worker.run(); + +// In your client process, pass the same plugin: +const client = new Client({ + connection: await Connection.connect(), + plugins: [plugin], +}); +``` + +For the full setup and options, see `references/typescript/integrations/opentelemetry.md`. + ## Search Attributes (Visibility) See the Search Attributes section of `references/typescript/data-handling.md` @@ -111,3 +160,4 @@ See the Search Attributes section of `references/typescript/data-handling.md` 3. Configure Winston or similar for production log aggregation 4. Monitor Prometheus metrics for worker health 5. Use Event History for debugging workflow issues +6. Use the `OpenTelemetryPlugin` for distributed tracing across Client/Workflow/Activity boundaries.