@aerem/telemetry
v0.2.0
Published
Aerem's shared OpenTelemetry setup for Node services. The telemetry contract, enforced by code.
Readme
@aerem/telemetry
Aerem's shared OpenTelemetry setup for Node services. The telemetry contract, enforced by code.
Internal to Aerem. Published publicly because the @aerem scope is, and because there is nothing
secret in it — every endpoint and credential comes from the environment.
Install
npm i @aerem/telemetryUse
1. Register first. Before the framework boots:
// bin/server.ts — the very first import
import { startTelemetry } from '@aerem/telemetry'
startTelemetry()⚠ Ordering matters. The auto-instrumentations patch http, pg and amqplib at require time.
Register after those are loaded and the patches land on nothing — traces come back empty, which
looks exactly like no traffic.
2. Set the contract in your stack file, not a baked image:
OTEL_SERVICE_NAME=sunstore-core-service
AEREM_PRODUCT=sunstore
AEREM_TEAM=sunstore-backend
AEREM_ENV=prod
OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4318Miss any of the four and telemetry disables itself with a warning naming what is absent. It never throws — a missing variable must not stop a release.
3. Logs:
import { pinoTelemetryOptions } from '@aerem/telemetry'
export default defineConfig({ ...pinoTelemetryOptions })Adds trace_id/span_id to every line, and redacts credentials — including
error.config.headers.Authorization, which is where a failed Axios call carries a live token.
4. Jobs across a queue:
import { injectTraceHeaders, extractTraceHeaders, jobSpan } from '@aerem/telemetry'
// publishing
channel.sendToQueue(queue, payload, { headers: injectTraceHeaders() })
// consuming
await jobSpan(
extractTraceHeaders(msg.properties.headers),
{ job: name, queue, messageId: msg.properties.messageId },
() => runJob(data),
(err) => (deliveries > maxRetries ? 'dead_lettered' : 'retried'),
)Auto-instrumentation does not propagate across a broker. This is what puts a failed job under the request that queued it.
5. Health:
import { runChecks, readinessStatusCode } from '@aerem/telemetry'
const report = await runChecks({ postgres, redis, rabbitmq, migrations })
return response.status(readinessStatusCode(report)).json(report)/health/live checks nothing external — if it checked the database, one blip would restart every
replica at once.
Guarantees
- Never breaks the app. Missing config disables it, a dead collector is invisible, an internal throw is caught and warned.
- Never learns where telemetry is stored. It sends to the environment's collector. Changing vendor is a collector deploy, not a release.
Full standard: CONTRACT.md in aerem-observability.
