OpenTelemetry
Distributed tracing and observability with OpenTelemetry
The @justscale/feature-otel package provides automatic distributed tracing for JustScale applications using OpenTelemetry. It creates spans for HTTP requests, records errors, and propagates trace context across services.
Installation
npm install @justscale/feature-otelBasic Setup
Add the otelFeature to your application to enable automatic tracing:
import { createCluster } from '@justscale/cluster';
import { otelFeature } from '@justscale/feature-otel';
const cluster = createCluster({
features: [
otelFeature({ serviceName: 'my-api' }),
],
controllers: [MyController],
});
await cluster.serve({ http: 3000 });What Gets Traced
- Automatic span creation for every HTTP request
- Error recording and exception tracking
- Log-to-span-event conversion
- Distributed tracing via W3C Trace Context headers
Production Setup with OTLP
For production, configure the OpenTelemetry SDK to export traces to your observability backend:
import { createCluster } from '@justscale/cluster';
import { otelFeature } from '@justscale/feature-otel';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
// 1. Initialize the OpenTelemetry SDK
const sdk = new NodeSDK({
serviceName: 'my-api',
traceExporter: new OTLPTraceExporter({
url: 'http://localhost:4318/v1/traces',
}),
});
sdk.start();
// 2. Create app with otel feature
const cluster = createCluster({
features: [otelFeature({ serviceName: 'my-api' })],
controllers: [],
});
await cluster.serve({ http: 3000 });This sends traces to any OTLP-compatible backend like Jaeger, Tempo, Honeycomb, or Datadog.
Manual Span Creation
Create custom spans for specific operations within your services:
import { withSpan, withSpanSync } from '@justscale/feature-otel';
// Async operations
async function processOrder(orderId: string) {
return withSpan('process-order', { orderId }, async () => {
await validateOrder(orderId);
await chargePayment(orderId);
await sendConfirmation(orderId);
});
}
// Sync operations
function calculateTax(amount: number) {
return withSpanSync('calculate-tax', { amount }, () => {
return amount * 0.1;
});
}
async function validateOrder(orderId: string) { /* ... */ }
async function chargePayment(orderId: string) { /* ... */ }
async function sendConfirmation(orderId: string) { /* ... */ }Accessing the Active Span
Get the current span to add attributes or events:
import { getActiveSpan } from '@justscale/feature-otel';
function processPayment(amount: number) {
const span = getActiveSpan();
if (span) {
span.setAttribute('payment.amount', amount);
span.addEvent('payment.started');
}
// ... process payment logic
span?.addEvent('payment.completed');
}Distributed Tracing
Propagate trace context when calling external services:
import { createTracedFetch, getTraceHeaders } from '@justscale/feature-otel';
// Option 1: Use createTracedFetch for automatic propagation
const tracedFetch = createTracedFetch();
const response = await tracedFetch('https://api.example.com/data');
// Option 2: Manual header injection
const headers = getTraceHeaders();
const manualResponse = await fetch('https://api.example.com/data', {
headers: {
...headers,
'Content-Type': 'application/json',
},
});Configuration Options
interface OtelFeatureConfig {
// Service name for spans (required)
serviceName: string;
// Whether to record exceptions in spans (default: true)
recordExceptions?: boolean;
// Whether to record logs as span events (default: true)
recordLogs?: boolean;
// Attributes to add to all spans
defaultAttributes?: Record<string, string | number | boolean>;
}import { otelFeature } from '@justscale/feature-otel';
const feature = otelFeature({
serviceName: 'my-api',
recordExceptions: true,
recordLogs: true,
defaultAttributes: {
'deployment.environment': 'production',
'service.version': '1.0.0',
},
});Viewing Traces
Use an OTLP-compatible backend to view your traces:
- Jaeger - Open source, self-hosted
- Grafana Tempo - Scalable trace storage
- Honeycomb - SaaS observability platform
- Datadog - Full observability suite
Local Development with Jaeger
# Run Jaeger with Docker
docker run -d --name jaeger \
-p 16686:16686 \
-p 4318:4318 \
jaegertracing/all-in-one:latest
# View traces at http://localhost:16686