Skip to main content

OpenTelemetry Integration

Automatic distributed tracing for Altstack servers with OpenTelemetry.

Installation

pnpm add @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http

Quick Start

import { createServer, router, init, ok } from "@alt-stack/server-hono";

const { procedure } = init<AppContext>();

const app = createServer({
"/api": router({
"/todos": procedure
.output(z.array(TodoSchema))
.get(async () => ok(await db.todos.findMany())),
}),
}, { telemetry: true });

Configuration

createServer({ "/api": appRouter }, {
telemetry: {
enabled: true,
serviceName: "my-api",
ignoreRoutes: ["/health", "/metrics"],
},
});
OptionTypeDefaultDescription
enabledbooleanfalseEnable/disable telemetry
serviceNamestring"altstack-server"Service name for traces
ignoreRoutesstring[][]Routes to exclude from tracing

Span Attributes

Follows OpenTelemetry HTTP semantic conventions:

AttributeExampleDescription
http.request.methodGETHTTP method
http.route/api/todos/{id}Route pattern
url.path/api/todos/123Actual URL path
http.response.status_code200Response status

Custom Attributes

Access the span via ctx.span:

"/todos/{id}": procedure
.input({ params: z.object({ id: z.string() }) })
.get(async ({ input, ctx }) => {
ctx.span?.setAttribute("todo.id", input.params.id);
ctx.span?.addEvent("db-query");
const todo = await db.todos.findUnique({ where: { id: input.params.id } });
return ok(todo);
}),

OTel SDK Setup

Create tracing.ts and import it first in your entry point:

// tracing.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";

const sdk = new NodeSDK({
traceExporter: new OTLPTraceExporter({
url: "http://localhost:4318/v1/traces",
}),
});
sdk.start();

process.on("SIGTERM", () => sdk.shutdown().then(() => process.exit(0)));
// index.ts
import "./tracing.js"; // Must be first!
import { createServer } from "@alt-stack/server-hono";

Local Tracing with Jaeger

docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one:latest

View traces at http://localhost:16686

Works with Both Adapters

// Hono
import { createServer } from "@alt-stack/server-hono";
const app = createServer({ "/api": appRouter }, { telemetry: true });

// Express
import { createServer } from "@alt-stack/server-express";
const app = createServer({ "/api": appRouter }, { telemetry: true });

Graceful Degradation

If @opentelemetry/api is not installed, telemetry is silently disabled. ctx.span will be undefined, so use optional chaining:

ctx.span?.setAttribute("key", "value"); // Safe even without OTel