OpenTelemetry
A SERVER span for every request, named by its route, continued from the caller's traceparent.
Needs inkan 0.8
@inkanjs/otel needs inkan 0.8.0 or newer, below 1.0.
npm i @inkanjs/otel @opentelemetry/apiimport { } from "@inkanjs/otel";
.(({ : () => .path === "/health" }))
.("/teas/:id", () => {
.span.setAttribute("tea.id", ..);
return findTea(..);
});The one official package with a dependency: @opentelemetry/api (a peer, ^1.4.0),
the vendor-neutral API every OpenTelemetry SDK implements. No SDK, no exporter: which SDK
turns the spans into traces is up to your app, and without one every call is a no-op. Set
one up before the app starts, as the OpenTelemetry docs for Node describe (for example
@opentelemetry/sdk-node with an OTLP exporter, loaded with node --import ./tracing.js).
What a span holds
GET /teas/:id SERVER
http.request.method GET (_OTHER and http.request.method_original for unknown ones)
url.path /teas/7
url.scheme https (ctx.protocol)
http.route /teas/:id
http.response.status_code 200
client.address 203.0.113.9 (ctx.ip, so with trustProxy the real client)
user_agent.original curl/8.5
server.address, .port api.example.com, 8443 (the Host header)
network.protocol.version 1.1- Name. The method and the route's pattern (
GET /teas/:id), never the path itself, so names stay few. A request no route takes is named by its method alone. - Parent. The caller's trace context is read with the global propagator (W3C
traceparentby default), from the headers as they arrived. - Errors. A 5xx sets the status to
ERROR,error.typeto the problem's type (internal,bad-gateway, …) and records the problem as an exception event. A 4xx is the client's mistake and staysUNSET, as the conventions ask for server spans. - End. In
onResponse, once the answer is written: a streamed answer's span lasts until its last chunk. - The query string is not recorded: it may hold tokens. Add it with
attributesif yours does not.
On the context
ctx.span | this request's span: setAttribute, addEvent, recordException. Non-recording for an ignored request |
ctx.traceContext | the OpenTelemetry context with the span in it, for children |
The span is not made the active one for the handler's async work. Start children under it with the context:
const = trace.getTracer("tea-shop");
.("/teas/:id", () =>
.startActiveSpan("load tea", {}, .traceContext, async () => {
try {
return await db.tea(..);
} finally {
.end();
}
}),
);A thrown error reaches the span as a problem only (internal): inkan keeps the error itself
for onError. Record it there with spanOf, which takes the ctx onError gets, undefined too (an
error from outside any request has no span):
import { } from "@inkanjs/otel";
const = ({ : (, ) => void ()?.recordException( as Error) });Behaviour
- A shared plugin: register it on the app for every route, or inside a plugin for its routes. Register it first, so its span covers the hooks of the plugins after it.
- The span starts in an
onRequesthook, after routing and before the body is read. - Jobs get spans for their routes like any other; the work that runs after the 202 does not. Start a span in it yourself if you want one.
Options
| option | default | |
|---|---|---|
tracer | the global provider's @inkanjs/otel tracer | |
propagate | true | continue the caller's trace from its headers |
ignore | none | (ctx) => boolean: requests to leave untraced, such as health checks |
spanName | METHOD /route/:pattern | (ctx) => string |
attributes | none | (ctx) => attributes, set when the span starts, so samplers see them |
OpenAPI
Unchanged.