@elephantroom-jumbo/telemetry
v0.5.0
Published
Batteries-included OpenTelemetry SDK for Jumbo Node services
Readme
@elephantroom-jumbo/telemetry
Batteries-included OpenTelemetry SDK for Jumbo Node services. Wraps
@opentelemetry/sdk-node with sane defaults, a Pino logger that forwards to the
LGTM stack, HTTP RED metrics, and Cube query tracing — so a service gets traces,
metrics, and logs with one init() call.
- npm:
@elephantroom-jumbo/telemetry(public) - ESM only (
"type": "module"), Node ≥ 20.
Install
npm install @elephantroom-jumbo/telemetryQuick start
init() must run before importing anything else — auto-instrumentation
patches modules at import time.
import { init } from '@elephantroom-jumbo/telemetry';
init({ serviceName: 'jumbo-production' }); // first import in the entry point
import { getLogger } from '@elephantroom-jumbo/telemetry';
const log = getLogger('orders'); // Pino child, auto-injects trace_id/span_id
log.info({ orderId }, 'order placed');Environment
OTEL_EXPORTER_OTLP_ENDPOINT=https://<collector-host>
OTEL_EXPORTER_OTLP_HEADERS=Authorization=Bearer <token>
NODE_ENV=production # → deployment.environment.name resource attributeAPI
| Export | Purpose |
|--------|---------|
| init(config) | Start the SDK. Call first. Idempotent. |
| getLogger(component) | Pino child logger; trace context auto-injected; forwards to Loki. |
| getTracer(name) | Manual OTel tracer for custom spans. |
| getMeter(name) | Manual OTel meter for custom metrics. |
| metricsMiddleware(options?) | Express middleware: HTTP RED metrics + span-name repair + optional per-user usage attribution. |
| wrapCubeQuery(fn) | Wrap a Cube /load call in a traced span. |
| shutdown() | Graceful flush — call on SIGTERM. |
HTTP metrics + per-user usage — metricsMiddleware
Mount it after your body parsers (and, if you want user attribution, after your auth middleware — see below):
import { metricsMiddleware } from '@elephantroom-jumbo/telemetry';
app.use(metricsMiddleware());Records RED metrics (http.server.request.total, .duration, .error.total)
and repairs the server span name to the templated route
(GET /api/customers/:customerId) so per-request IDs never leak into span names
or spanmetrics cardinality.
Opt-in user attribution (resolveUser)
Pass resolveUser to make authed requests attributable to the logged-in user.
When it returns a non-null user, the middleware:
- tags the request span with
enduser.id(andenduser.rolewhen present) — for Tempo trace↔log correlation; and - emits a
feature_usedlog to Loki:{ event, user_id, role, http_route, http_method, status_code }.
// auth middleware must run first so req.user is populated
app.use(clerkAuth);
app.use(
metricsMiddleware({
resolveUser: (req) =>
req.user ? { id: String(req.user.id), role: req.user.role } : null,
}),
);resolveUser is called at most once per request, at response finish, and must
never throw (errors are swallowed). User identity is written only to the span
attribute and the feature_used log body (which lands as Loki structured
metadata) — never to the RED metric labels, so Prometheus cardinality stays
bounded. feature_used is emitted only for authenticated requests that matched
a route, so unauthenticated traffic (health checks, login) produces none.
The feature_used event is the per-user usage signal — "who used which
feature" — queryable in Loki by user_id + http_route since a given time.
Graceful shutdown
process.on('SIGTERM', async () => { await shutdown(); process.exit(0); });Development
npm run build # tsc → dist/
npm run typecheck # tsc --noEmit
npm test # node:test + tsx (test/*.test.ts)