Telemetry
Configure tracing and metrics with @namzu/telemetry — OTLP or console exporters, and the built-in platform metrics helpers.
As of 0.4.0, the OpenTelemetry exporter pipeline ships in a separate
package: @namzu/telemetry.
@namzu/sdk depends only on @opentelemetry/api (peer). Consumers who
never emit telemetry no longer transitively install the full OTEL Node
SDK. See docs/migration/0.4.md if you are
upgrading from 0.3.x.
1. Install
@opentelemetry/api is a peer of both @namzu/sdk and @namzu/telemetry.
On pnpm 9+ and npm 7+ it auto-installs; on older clients, install it
explicitly yourself.
2. The Public Telemetry Surface
All telemetry exports come from @namzu/telemetry (not @namzu/sdk).
| Export | Purpose |
|---|---|
TelemetryProvider | Explicit telemetry lifecycle owner |
registerTelemetry() | async — create the global provider and start it |
getTelemetry() | Read the current global provider |
getTracer() | Get the shared tracer |
getMeter() | Get the shared meter |
createPlatformMetrics() | Record common Namzu runtime metrics |
Types: TelemetryConfig, ExporterType, PlatformMetrics.
Attribute constants (GENAI, NAMZU) and span-name helpers
(agentRunSpanName, agentIterationSpanName, chatSpanName,
toolSpanName) ship under the subpath:
3. Bootstrap Telemetry
registerTelemetry() is asynchronous. It must be awaited — the underlying
TelemetryProvider.start() returns a Promise<void> because the OTEL
Node SDK attaches its exporters asynchronously. Firing-and-forgetting
would detach startup failures into an unhandled rejection.
Safe application pattern:
- initialize once during app startup,
awaitcompletion - construct
createPlatformMetrics()AFTERregisterTelemetryresolves - shut down during graceful termination
4. Exporter Types
TelemetryConfig.exporterType:
| Value | Behavior |
|---|---|
console | Emit spans and metrics to console exporters |
otlp | Export through OTLP HTTP exporters |
none | Disable exporter startup while keeping the API surface available |
OTLP:
5. What Happens If You Never Call registerTelemetry
The helper accessors are intentionally forgiving:
getTelemetry()returnsnullgetTracer()falls back to the@opentelemetry/apino-op tracergetMeter()falls back to the@opentelemetry/apino-op meter
That means SDK code can keep calling tracing or metrics helpers safely, but spans and metric writes are silently discarded until a real provider is registered. This is the standard OpenTelemetry library contract, not a Namzu quirk.
6. Eager-Bind Caveat for createPlatformMetrics
createPlatformMetrics() builds counters and histograms at construction
time against whatever getMeter() returns. If you construct it before
registerTelemetry(), the counters bind to the no-op meter and every
subsequent .add() / .record() is discarded — for the lifetime of
that metrics instance. Registering a real provider later does not
retroactively rewire existing counters.
Always await registerTelemetry({...}) first, then
createPlatformMetrics(). Or wrap the latter in a lazy factory if the
call order is not under your control.
7. Built-In Platform Metrics
Four common operational signals: token usage, tool-call success/failure, run duration, LLM latency.
8. Add Custom Spans
9. What the SDK Already Instruments
Even without custom spans, the SDK runtime already uses the shared tracer in core execution paths:
- agent run setup (
runtime/query/index.ts) - iteration execution (
runtime/query/iteration/index.ts) - tool execution (
registry/tool/execute.ts)
Telemetry becomes useful as soon as you await registerTelemetry() at
startup; nothing else in your code needs to change to pick up the
instrumentation already there.
10. Common Mistakes
| Mistake | Why it hurts |
|---|---|
calling registerTelemetry() without await | startup errors silently become unhandled rejections |
constructing createPlatformMetrics() before registerTelemetry | counters bind to the no-op meter and never rewire |
expecting getTelemetry() to always return a provider | it returns null until registration completes |
| using custom spans with a different telemetry bootstrap than the SDK | traces fragment across providers |