Katman
Plugins

OpenTelemetry

Distributed tracing — each procedure call becomes a span with automatic error recording.

OpenTelemetry (OTel) is the standard for distributed tracing. Katman's otelWrap creates a span for each procedure call, so you can see every RPC call in your tracing dashboard (Jaeger, Zipkin, Honeycomb, Datadog, etc.).

How it works

otelWrap is a wrap middleware. It creates a span, runs your procedure inside it, then closes the span. If the procedure throws, the span records the error before rethrowing.

Setup

Install OpenTelemetry

npm install @opentelemetry/api

You'll also need an OTel SDK and exporter for your backend (Jaeger, OTLP, etc.) — but that's standard OTel setup, not Katman-specific.

Create the wrap

import {  } from "katman/otel"
import {  } from "@opentelemetry/api"

const  = (.getTracer("my-service"))

Add it to procedures

const  = k.query({
  : [tracing],
  : ({  }) => .db.users.findMany(),
})

const  = k.mutation({
  : [auth, tracing],
  : ({ ,  }) => .db.users.create(),
})

Each call to these procedures now produces a span in your tracing backend.

What gets recorded

Every span includes:

AttributeValue
rpc.system"katman"
Status code0 (OK) on success, 2 (ERROR) on failure

On errors, the span also includes an exception event:

Event: exception
  exception.message: "Not found"

This means errors are automatically visible in your tracing dashboard — you don't need to add manual error reporting to each procedure.

Custom span name

The default span name is "rpc.call". You can change it:

import {  } from "katman/otel"

const  = (tracer, "api.procedure")

Combining with other middleware

otelWrap is a regular wrap middleware. Stack it with guards and other wraps normally:

const  = k.mutation({
  : [auth, rateLimit, tracing],
  //    guards first, then wraps
  : ({ ,  }) => .db.users.create(),
})

Guards (auth, rateLimit) run before the span wraps around the resolver. If you want the span to include guard execution time, put tracing before the guards in the array.

Katman has zero OpenTelemetry dependency. The plugin defines a lightweight Tracer and Span interface. You bring @opentelemetry/api yourself, which means Katman never pins you to a specific OTel version.

What's next?

On this page