Telemetry & Logging
Primebrick ships an in-house async logger and OpenTelemetry (OTel)
compatibility layer in @primebrick/sdk. Every log line carries a UTC
ISO 8601 timestamp and — when a trace is active — trace_id/span_id, so
logs correlate end-to-end across BE → NATS → microservices.
Async logger
console.log is synchronous and lock-prone under load. The SDK logger
buffers lines and flushes them in a single batched write per tick
(process.stdout.write on Node, Bun.write(Bun.stdout) on Bun) — callers
never wait for I/O.
Code
Output (pretty format, default):
Code
LOG_FORMAT-equivalent config key log_format=json switches to NDJSON for
SIEM pipelines:
Code
Console bridge
installConsoleBridge() rebinds console.log/info/warn/error/debug to the
async logger, so existing console.* call sites get timestamps, trace
context and async writes for free. It is installed automatically inside
createMicroservice() and skipped under vitest (VITEST env).
New code should use logger.* directly with structured meta fields
(snake_case keys) — it produces better SIEM queries than plain strings.
OpenTelemetry
initTelemetry(config, serviceName, version) builds the OTel pipeline:
BatchSpanProcessor + OTLP/HTTP exporters for traces and logs. Globals
(context manager, W3C propagator, tracer provider) are registered once
behind a ProxyTracerProvider, so restartTelemetry(config, …) can rebuild
the pipeline on config changes without restarting the process.
- HTTP:
createHttpServerwraps every request in a SERVER span, extracting the inbound W3Ctraceparent. Works identically on Node and Bun (no reliance onnode:httpmonkey-patching). - NATS:
NatsClient.publish/requestinjecttraceparent/tracestateinto message headers;subscribe/subscribeRequestextract them and run handlers inside the parent context in anats.consume/nats.servespan. - fetch: use
fetchTraced(url, init)for outbound calls — it injects the current trace context (needed on Bun where undici instrumentation is unavailable; harmless on Node). - Auto-instrumentations (Node only):
getNodeAutoInstrumentationscoverspg,redis,undici,express, etc.instrumentation-httpis disabled to avoid double spans (our manual server span covers inbound).
Configuration (single point)
Telemetry is configured in the BE config_entries table — no env files.
The BE serves it to microservices via SharedConfig.telemetry
(NATS config.get) and broadcasts config.changed on writes; services
re-apply config in-process via restartTelemetry.
config_entries.key | Type | Effect |
|---|---|---|
telemetry_enabled | boolean | Master switch — false = zero OTel overhead |
otel_exporter_otlp_endpoint | url | Collector base URL, e.g. http://collector:4318 |
otel_exporter_otlp_headers | json | Auth headers map (API key etc.) |
otel_traces_sampler | badge | always_on / always_off / traceidratio |
otel_traces_sampler_arg | number | Ratio when sampler = traceidratio (0..1) |
log_format | badge | pretty (default) / json |
log_level | badge | debug / info / warn / error |
Primebrick does not ship a collector or dashboards — set
otel_exporter_otlp_endpoint to the OTLP endpoint provided by your
APM/SIEM (Datadog, Grafana, Elastic, Splunk… all ingest OTLP).