nestjs-wide-events
NestJS Wide Events Logging
Emit ONE context-rich structured JSON event per request per service, instead of scattering dozens of log lines across the codebase.
Core Principles
- One event per request per service. Build context throughout the lifecycle; emit once at the end.
- Business context is mandatory. Include subscription tier, account age, feature flags, cart value -- anything needed at 2am during an incident.
- High cardinality is the point. Fields like
user_id,request_id,order_idwith millions of unique values make debugging possible. - High dimensionality wins. 50 fields on one event beats 5 fields across 10 events.
- Structured JSON only. Every field is a queryable key-value pair.
- Tail sampling controls cost. Always keep errors, slow requests, VIP users. Sample the rest.
- No log-level abuse. The wide event IS the log. Level indicates outcome severity only.
Architecture Overview
Components
WideEventInterceptor(global): Creates context at request start, emits the single event at request end.WideEventService(request-scoped): Holds the mutable event context. Any service/guard/pipe injects it to enrich the event.WideEventModule: Wires everything together. Import at root level.TailSampler: Decides whether to persist the event (always keeps errors, slow requests, VIP users).WideEventExceptionFilter(global): Enriches the event with error details before the interceptor emits.WideEventMiddleware: Resolves the request-scoped service and attaches it to the request object for the singleton interceptor.
Request Lifecycle
Request arrives
-> Middleware resolves request-scoped WideEventService, attaches to req
-> Interceptor seeds event with HTTP metadata, starts timer
-> Guards/pipes inject WideEventService, add user context
-> Controller/services inject WideEventService, add business context
-> Response or exception
-> ExceptionFilter catches errors, enriches event
-> Interceptor finalizes: duration, status, outcome
-> TailSampler decides: persist or drop
-> Single JSON event emitted
Wide Event Field Groups
- Request envelope (auto):
timestamp,request_id,trace_id,method,path,status_code,duration_ms,ip,user_agent,service,version - User context (auth guard/controller):
user.id,user.subscription,user.role,user.org_id - Business context (controller/service): domain-specific fields like
cart.*,payment.* - Error context (exception filter):
error.type,error.code,error.message,error.retriable - Performance (services doing I/O):
db.query_count,db.total_ms,cache.hit,external_calls[] - Feature flags:
feature_flags.*as flat booleans - Outcome (interceptor):
outcome(success|error|client_error),level(info|warn|error)
Key Rules
- Use
REQUESTscope forWideEventService-- each request gets its own instance. - Only the interceptor calls
logger.info(event). Services only enrich. - Sanitize sensitive fields with an allowlist, not a denylist.
- Use
AsyncLocalStoragewhen request-scoped DI is unavailable (queues, event handlers). - Nest fields under namespaces:
user.idnotuserId,payment.latency_msnotpaymentLatencyMs. - Timings use
_mssuffix, always integers. - For queue/cron jobs: emit one wide event per job execution, same pattern.
Tail Sampling
- Always keep (100%): status >= 500, error field present, duration > p99 threshold, VIP users, monitored feature flags
- Sample the rest: configurable rate, default 5%
Anti-Patterns
- Do NOT scatter
console.log()/this.logger.log()through services. - Do NOT use log levels to categorize request stages.
- Do NOT put high-volume debug tracing in the wide event.
- Do NOT log request/response bodies wholesale -- extract only needed fields.
- Do NOT treat OpenTelemetry as a substitute; your wide event IS your enriched OTel span.
Reference Documents
Read these only when you need implementation details for a specific area:
core-setup.md-- Full code for WideEventService, WideEventInterceptor, WideEventExceptionFilter, TailSampler, WideEventMiddleware, WideEventModule, and app bootstrap. Read this when setting up wide events from scratch.usage-examples.md-- How to enrich events from controllers, services, and auth guards. Read this when adding wide event enrichment to existing code.advanced-patterns.md-- AsyncLocalStorage bridge, queue/cron job pattern, and Pino integration. Read this for non-HTTP contexts or production logger setup.testing.md-- Unit tests for WideEventService and TailSampler. Read this when writing tests.
Quick Start
- Read
core-setup.mdfor full component code. - Create
WideEventModulewith all components, register at app root. - In controllers/services, inject
WideEventServiceand callevent.enrich({...}). - Configure
TailSamplersampling rules. - Remove all scattered log statements -- replace with event enrichments.
More from cmglezpdev/custom-skills
code-audit
>
9nestjs-lgtm-metrics
Add application-level Prometheus metrics to a NestJS app using the OpenTelemetry SDK and an OTel Collector. Covers HTTP RED metrics, Node.js runtime metrics, business metrics, and advanced observability patterns. Use this skill whenever the user wants to add metrics, dashboards, or monitoring to a NestJS application, mentions Prometheus, Grafana metrics, OpenTelemetry metrics, OTel Collector, or asks about SLIs/SLOs, or application-level monitoring in a NestJS context. Also trigger when the user wants custom counters, histograms, gauges, or summaries in NestJS. This skill focuses exclusively on metrics the APPLICATION must emit. It does not cover logging, tracing, infra-level metrics from cAdvisor, node-exporter, postgres-exporter, or redis-exporter.
8documentation
>
7