OpenTelemetry
One of the powerful features of PURISTA is the deeply integrated usage of OpenTelemetry.
In this section, you will learn how to setup OpenTelemetry in Temporal.
This allows tracing of whole processes - no matter where parts of the process are executed.
Add dependencies
Temporal provides official OpenTelemetry support.
For using OpenTelemetry in Temporal, We need to add some dependencies.
bash
npm i -s @opentelemetry/resources \
@opentelemetry/sdk-node \
@opentelemetry/sdk-trace-node \
@opentelemetry/semantic-conventions \
@temporalio/interceptors-opentelemetry
Setup tracing
In this example we use Jaeger and we use the same config as our PURISTA application stored in src/config/jaegerExporterOptions.ts
.
Here you can see the changes, which are needed to enable OpenTelemetry.
typescript
import { fileURLToPath } from 'node:url'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { Resource } from '@opentelemetry/resources'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions'
import { Context } from '@temporalio/activity'
import { makeWorkflowExporter, OpenTelemetryActivityInboundInterceptor } from '@temporalio/interceptors-opentelemetry'
import { NativeConnection, Worker } from '@temporalio/worker'
import type { EventBridge } from '@purista/core'
import { initLogger } from '@purista/core'
import { NatsBridge } from '@purista/natsbridge'
import jaegerExporterOptions from './config/jaegerExporterOptions.js'
import temporalConfig from '../config/temporalConfig.js'
import natsBridgeConfig from '../config/natsBridgeConfig.js'
import * as activities from './activities/index.js'
const getPuristaBasedActivities = (eventbridge: EventBridge) => {
const invoke = getInvoke(eventBridge)
return {
sendEmailVerification: (email: string) =>
invoke<{ userId: string }> // our helper with return type
('Email', '1', 'sendVerificationEmail', { email }),
}
}
export type ActivitiesType = typeof activities & ReturnType<typeof getPuristaBasedActivities>
async function run() {
// setup OpenTelemetry
const resource = new Resource({
[SemanticResourceAttributes.SERVICE_NAME]: 'temporal-worker',
})
const exporter = new OTLPTraceExporter(jaegerExporterOptions)
const otel = new NodeSDK({ traceExporter: exporter, resource })
await otel.start()
const spanProcessor = new SimpleSpanProcessor(exporter)
const logger = initLogger('debug')
// inject eventBride and logger
const eventBridge = new NatsBridge({ ...natsBridgeConfig, logger })
const eventBridge = new NatsBridge({
...natsBridgeConfig,
logger,
spanProcessor
})
await eventBridge.start()
const connection = await NativeConnection.connect(temporalConfig.connect)
const worker = await Worker.create({
connection,
namespace: temporalConfig.namespace,
taskQueue: temporalConfig.taskQueue,
workflowsPath: fileURLToPath(new URL('./workflows', import.meta.url)),
activities: {
...activities,
...getPuristaBasedActivities(eventBridge)
},
interceptors: {
// example contains both workflow and interceptors
workflowModules: [fileURLToPath(new URL('./workflows', import.meta.url))],
activityInbound: [(ctx) => new OpenTelemetryActivityInboundInterceptor(ctx)],
},
sinks: {
exporter: makeWorkflowExporter(exporter, resource),
},
})
await worker.run()
}
run().catch((err) => {
// eslint-disable-next-line no-console
console.error(err)
process.exit(1)
})
And that´s it 🎉
You should now receive traces all the way down.
From the web server down to the Temoral workflow.