Skip to content
Open
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
106 changes: 106 additions & 0 deletions content/integrations/frameworks/temporal.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,112 @@ async def main():
await main()
```

## TypeScript/JavaScript Example

For TypeScript or JavaScript Temporal applications, use Temporal's OpenTelemetry plugin together with the Langfuse OpenTelemetry span processor. The same Langfuse credentials from step 2 apply.

Install the Temporal SDK, the Temporal OpenTelemetry plugin, and the Langfuse OpenTelemetry integration:

```bash
npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/interceptors-opentelemetry-v2 @langfuse/tracing @langfuse/otel @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions
```

Create an OpenTelemetry setup file that exports spans to Langfuse:

```typescript filename="instrumentation.ts"
import { NodeSDK } from "@opentelemetry/sdk-node";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { LangfuseSpanProcessor } from "@langfuse/otel";

export const resource = resourceFromAttributes({
[ATTR_SERVICE_NAME]: "temporal-research-agent",
});

export const spanProcessor = new LangfuseSpanProcessor();

export function setupOtelSdk() {
const sdk = new NodeSDK({
resource,
spanProcessors: [spanProcessor],
});

sdk.start();
return sdk;
}
```

Register Temporal's OpenTelemetry plugin on the worker. Define your workflows and activities as usual; the plugin propagates trace context across workflow and activity execution.

```typescript filename="worker.ts"
import { Worker } from "@temporalio/worker";
import { OpenTelemetryPlugin } from "@temporalio/interceptors-opentelemetry-v2";
import * as activities from "./activities";
import { resource, setupOtelSdk, spanProcessor } from "./instrumentation";

async function main() {
const otelSdk = setupOtelSdk();

const worker = await Worker.create({
workflowsPath: require.resolve("./workflows"),
activities,
taskQueue: "research-task-queue",
plugins: [new OpenTelemetryPlugin({ resource, spanProcessor })],
});

try {
await worker.run();
} finally {
await otelSdk.shutdown();
}
}

main().catch((error) => {
console.error(error);
process.exit(1);
});
```

Use the same plugin on the Temporal client so the client span becomes the parent of the workflow trace.

```typescript filename="client.ts"
import { randomUUID } from "node:crypto";
import { Client, Connection } from "@temporalio/client";
import { OpenTelemetryPlugin } from "@temporalio/interceptors-opentelemetry-v2";
import { ResearchWorkflow } from "./workflows";
import { resource, setupOtelSdk, spanProcessor } from "./instrumentation";

async function main() {
const otelSdk = setupOtelSdk();
const connection = await Connection.connect({
address: process.env.TEMPORAL_ADDRESS ?? "localhost:7233",
});

const client = new Client({
connection,
namespace: process.env.TEMPORAL_NAMESPACE ?? "default",
plugins: [new OpenTelemetryPlugin({ resource, spanProcessor })],
});

try {
const result = await client.workflow.execute(ResearchWorkflow, {
workflowId: randomUUID(),
taskQueue: "research-task-queue",
args: ["Caribbean vacation spots in April"],
});

console.log(result);
} finally {
await otelSdk.shutdown();
}
}

main().catch((error) => {
console.error(error);
process.exit(1);
});
```

## 8. View Traces in Langfuse

After running the workflow, you can view the complete trace in Langfuse. The trace will show:
Expand Down
111 changes: 111 additions & 0 deletions cookbook/integration_temporal.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,117 @@
"await main()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## TypeScript/JavaScript Example\n",
"\n",
"For TypeScript or JavaScript Temporal applications, use Temporal's OpenTelemetry plugin together with the Langfuse OpenTelemetry span processor. The same Langfuse credentials from step 2 apply.\n",
"\n",
"Install the Temporal SDK, the Temporal OpenTelemetry plugin, and the Langfuse OpenTelemetry integration:\n",
"\n",
"```bash\n",
"npm install @temporalio/client @temporalio/worker @temporalio/workflow @temporalio/activity @temporalio/interceptors-opentelemetry-v2 @langfuse/tracing @langfuse/otel @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions\n",
"```\n",
"\n",
"Create an OpenTelemetry setup file that exports spans to Langfuse:\n",
"\n",
"```typescript filename=\"instrumentation.ts\"\n",
"import { NodeSDK } from \"@opentelemetry/sdk-node\";\n",
"import { resourceFromAttributes } from \"@opentelemetry/resources\";\n",
"import { ATTR_SERVICE_NAME } from \"@opentelemetry/semantic-conventions\";\n",
"import { LangfuseSpanProcessor } from \"@langfuse/otel\";\n",
"\n",
"export const resource = resourceFromAttributes({\n",
" [ATTR_SERVICE_NAME]: \"temporal-research-agent\",\n",
"});\n",
"\n",
"export const spanProcessor = new LangfuseSpanProcessor();\n",
"\n",
"export function setupOtelSdk() {\n",
" const sdk = new NodeSDK({\n",
" resource,\n",
" spanProcessors: [spanProcessor],\n",
" });\n",
"\n",
" sdk.start();\n",
" return sdk;\n",
"}\n",
"```\n",
"\n",
"Register Temporal's OpenTelemetry plugin on the worker. Define your workflows and activities as usual; the plugin propagates trace context across workflow and activity execution.\n",
"\n",
"```typescript filename=\"worker.ts\"\n",
"import { Worker } from \"@temporalio/worker\";\n",
"import { OpenTelemetryPlugin } from \"@temporalio/interceptors-opentelemetry-v2\";\n",
"import * as activities from \"./activities\";\n",
"import { resource, setupOtelSdk, spanProcessor } from \"./instrumentation\";\n",
"\n",
"async function main() {\n",
" const otelSdk = setupOtelSdk();\n",
"\n",
" const worker = await Worker.create({\n",
" workflowsPath: require.resolve(\"./workflows\"),\n",
" activities,\n",
" taskQueue: \"research-task-queue\",\n",
" plugins: [new OpenTelemetryPlugin({ resource, spanProcessor })],\n",
" });\n",
"\n",
" try {\n",
" await worker.run();\n",
" } finally {\n",
" await otelSdk.shutdown();\n",
" }\n",
"}\n",
"\n",
"main().catch((error) => {\n",
" console.error(error);\n",
" process.exit(1);\n",
"});\n",
"```\n",
"\n",
"Use the same plugin on the Temporal client so the client span becomes the parent of the workflow trace.\n",
"\n",
"```typescript filename=\"client.ts\"\n",
"import { randomUUID } from \"node:crypto\";\n",
"import { Client, Connection } from \"@temporalio/client\";\n",
"import { OpenTelemetryPlugin } from \"@temporalio/interceptors-opentelemetry-v2\";\n",
"import { ResearchWorkflow } from \"./workflows\";\n",
"import { resource, setupOtelSdk, spanProcessor } from \"./instrumentation\";\n",
"\n",
"async function main() {\n",
" const otelSdk = setupOtelSdk();\n",
" const connection = await Connection.connect({\n",
" address: process.env.TEMPORAL_ADDRESS ?? \"localhost:7233\",\n",
" });\n",
"\n",
" const client = new Client({\n",
" connection,\n",
" namespace: process.env.TEMPORAL_NAMESPACE ?? \"default\",\n",
" plugins: [new OpenTelemetryPlugin({ resource, spanProcessor })],\n",
" });\n",
"\n",
" try {\n",
" const result = await client.workflow.execute(ResearchWorkflow, {\n",
" workflowId: randomUUID(),\n",
" taskQueue: \"research-task-queue\",\n",
" args: [\"Caribbean vacation spots in April\"],\n",
" });\n",
"\n",
" console.log(result);\n",
" } finally {\n",
" await otelSdk.shutdown();\n",
" }\n",
"}\n",
"\n",
"main().catch((error) => {\n",
" console.error(error);\n",
" process.exit(1);\n",
"});\n",
"```\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down