Skip to content

Hono

The autotel-hono package provides an otel() middleware for automatic request tracing and HTTP metrics in Hono applications.

Terminal window
npm install autotel-hono autotel
import { Hono } from 'hono';
import { init, getRequestLogger } from 'autotel';
import { otel } from 'autotel-hono';
init({ service: 'my-api' });
const app = new Hono();
app.use('*', otel());
app.get('/api/users', async (c) => {
const log = getRequestLogger();
log.set({ route: '/api/users' });
const users = await db.users.findAll();
log.emitNow();
return c.json(users);
});

Use useLogger() from autotel-adapters/hono when the otel() middleware already creates a span (avoids duplicate spans):

import { otel } from 'autotel-hono';
import { useLogger } from 'autotel-adapters/hono';
app.use('*', otel());
app.get('/orders/:id', (c) => {
const log = useLogger(c);
log.set({ route: c.req.path });
return c.json({ ok: true });
});

A handler returning an SSE, NDJSON, or AI stream returns before the work finishes. Emit the request snapshot at that moment and you capture a request that has barely started, while everything the stream adds later lands nowhere.

autotelMiddleware() from autotel-adapters/hono waits for the body. Use it in place of otel() when you stream: it creates the span, the request logger, and emits the snapshot once the stream closes, errors, or gets cancelled.

import { autotelMiddleware, useLogger } from 'autotel-adapters/hono';
app.use('*', autotelMiddleware());
app.get('/chat', (c) => {
const log = useLogger(c);
return streamSSE(c, async (stream) => {
for await (const token of model.stream(prompt)) {
await stream.writeSSE({ data: token });
}
log.set({ 'chat.tokens': tokenCount }); // still lands in the snapshot
});
});

A client that disconnects mid-stream still produces a snapshot, so an abandoned generation reads as a cancelled stream rather than a request that never finished. The middleware wraps the response without locking the original body, so the client receives exactly what it would have without it.

Pass autoEmit: false when you want to call .emitNow() yourself.

The otel() middleware accepts options for customizing tracing behavior:

Option Description
tracer Custom tracer instance
tracerProvider Custom tracer provider
meter Custom meter instance
spanNameFactory Custom span name from request
captureRequestHeaders Headers to capture as span attributes
captureResponseHeaders Response headers to capture
captureActiveRequests Track active request gauge
captureRequestDuration Track request duration histogram
serviceName Override service name
serviceVersion Override service version
disableTracing Disable span creation (metrics only)

HTTP request spans include:

  • http.request.method: GET, POST, etc.
  • url.path: Request path
  • http.response.status_code: Response status
  • http.request.duration_ms: Request duration
  • example-hono: Hono with otel() middleware, useLogger() request-scoped logging, and manual trace() in handlers.