diff --git a/typescript/Dockerfile b/typescript/Dockerfile index 153d889..230f894 100644 --- a/typescript/Dockerfile +++ b/typescript/Dockerfile @@ -1,4 +1,4 @@ -FROM node:lts AS builder +FROM node:18.16-bullseye-slim AS builder USER node WORKDIR /home/node/app COPY --chown=node:node . . @@ -6,7 +6,7 @@ RUN npm ci \ && npm run build CMD [ "npm", "run", "worker:watch" ] -FROM node:lts-slim +FROM node:18.16-bullseye-slim WORKDIR /home/node/app COPY --from=builder /home/node/app/lib lib COPY --from=builder /home/node/app/package.json package.json diff --git a/typescript/compose.yaml b/typescript/compose.yaml index 71eb9cd..31ca5ab 100644 --- a/typescript/compose.yaml +++ b/typescript/compose.yaml @@ -26,10 +26,32 @@ services: target: builder environment: TEMPORAL_ADDRESS: temporal:7233 + OTEL_EXPORTER_OTLP_ENDPOINT: grpc://datadog-agent:4317 + # Tag your metrics for dashboards & filters + OTEL_SERVICE_NAME: temporal-worker + OTEL_RESOURCE_ATTRIBUTES: deployment.environment=prod,service.version=1.2.3 + # (Optional) if you also send traces via OTel SDKs, keep same endpoint + # OTEL_TRACES_EXPORTER: otlp + # OTEL_METRICS_EXPORTER: otlp volumes: - .:/home/node/app links: - temporal depends_on: + datadog-agent: + condition: service_started temporal: condition: service_healthy + + datadog-agent: + image: gcr.io/datadoghq/agent:latest + environment: + DD_API_KEY: ${DD_API_KEY} # set in your shell or .env + DD_SITE: datadoghq.com # or datadoghq.com, us3, us5, etc. + # Enable OTLP receiver on HTTP 4318 (or use GRPC 4317, see below) + DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT: "0.0.0.0:4317" + # Optional: enable logs ingest via OTLP if you also send logs + # DD_LOGS_ENABLED: "true" + # DD_OTLP_CONFIG_LOGS_ENABLED: "true" + ports: + - 4317:4317 diff --git a/typescript/package-lock.json b/typescript/package-lock.json index 8a22845..d5dd3f2 100644 --- a/typescript/package-lock.json +++ b/typescript/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@temporalio/activity": "^1.13.0", "@temporalio/client": "^1.13.0", + "@temporalio/interceptors-opentelemetry": "^1.13.0", "@temporalio/worker": "^1.13.0", "@temporalio/workflow": "^1.13.0", "nanoid": "^5.1.5" @@ -803,6 +804,72 @@ "node": ">= 8" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.30.1.tgz", + "integrity": "sha512-OOCM2C/QIURhJMuKaekP3TRBxBKxG/TWWA0TL2J6nXUtDnuCtccy49LUJF8xPFXMX+0LMcxFpCo8M9cGY1W6rQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.30.1.tgz", + "integrity": "sha512-5UxZqiAgLYGFjS4s9qm5mBVo433u+dSPUFWVWXmLAD4wB65oMCoXaJP1KJa9DIYYMeHu3z4BZcStG3LC593cWA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.30.1.tgz", + "integrity": "sha512-jVPgBbH1gCy2Lb7X0AVQ8XAfgg0pJ4nvl8/IiQA6nxOsPvS+0zMJaFSs2ltXe0J6C8dqjcnpyqINDJmU30+uOg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "1.30.1", + "@opentelemetry/resources": "1.30.1", + "@opentelemetry/semantic-conventions": "1.28.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.28.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz", + "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1168,6 +1235,28 @@ "node": ">= 18.0.0" } }, + "node_modules/@temporalio/interceptors-opentelemetry": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/@temporalio/interceptors-opentelemetry/-/interceptors-opentelemetry-1.13.0.tgz", + "integrity": "sha512-JdVQUfpaa30x7VCqf5ZGUfjT9TkwNpJ4scCmSBN+nTcZDohtysZVSHFBF0C1hNZNrMQtEW7yGBbEnu83rslftg==", + "license": "MIT", + "dependencies": { + "@opentelemetry/api": "^1.7.0", + "@opentelemetry/core": "^1.19.0", + "@opentelemetry/resources": "^1.19.0", + "@opentelemetry/sdk-trace-base": "^1.19.0" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "@temporalio/activity": "1.13.0", + "@temporalio/client": "1.13.0", + "@temporalio/common": "1.13.0", + "@temporalio/worker": "1.13.0", + "@temporalio/workflow": "1.13.0" + } + }, "node_modules/@temporalio/nexus": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/@temporalio/nexus/-/nexus-1.13.0.tgz", diff --git a/typescript/package.json b/typescript/package.json index 4cbd781..5e54e6f 100644 --- a/typescript/package.json +++ b/typescript/package.json @@ -26,6 +26,7 @@ "dependencies": { "@temporalio/activity": "^1.13.0", "@temporalio/client": "^1.13.0", + "@temporalio/interceptors-opentelemetry": "^1.13.0", "@temporalio/worker": "^1.13.0", "@temporalio/workflow": "^1.13.0", "nanoid": "^5.1.5" diff --git a/typescript/src/worker.ts b/typescript/src/worker.ts index 1b550fd..6770907 100644 --- a/typescript/src/worker.ts +++ b/typescript/src/worker.ts @@ -1,9 +1,29 @@ -import { NativeConnection, Worker } from '@temporalio/worker'; +import { + OpenTelemetryActivityInboundInterceptor, + OpenTelemetryActivityOutboundInterceptor, +} from '@temporalio/interceptors-opentelemetry'; +import { + NativeConnection, + Runtime, + Worker, + bundleWorkflowCode, +} from '@temporalio/worker'; import { createActivities } from './activities'; import { TASK_QUEUE_NAME } from './shared'; async function run() { + Runtime.install({ + telemetryOptions: { + metrics: { + otel: { + url: process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? '', + temporality: 'delta', + }, + }, + }, + }); + const connection = await NativeConnection.connect({ address: process.env.TEMPORAL_ADDRESS, tls: process.env.TEMPORAL_TLS === 'true', @@ -18,8 +38,19 @@ async function run() { taskQueue: TASK_QUEUE_NAME, // Workflows are registered using a path as they run in a separate JS context. workflowsPath: require.resolve('./workflows'), + // workflowBundle: await bundleWorkflowCode({ + // workflowsPath: require.resolve('./workflows'), + // }), // Register the activities - you may need to inject dependencies in here activities: createActivities(), + interceptors: { + activity: [ + (ctx) => ({ + inbound: new OpenTelemetryActivityInboundInterceptor(ctx), + outbound: new OpenTelemetryActivityOutboundInterceptor(ctx), + }), + ], + }, }); await worker.run();