Build on top

Custom enrichers

defineEnricher derives context from request headers, env, or anything else, and adds it to every wide event before drain — without touching call sites.

An enricher runs on every emitted event before it reaches drains. It's the right tool when you want a field on every event without touching every call site — geo, user agent, trace context, deploy id, tenant id…

enrich pipeline·idle
UserAgent
+3 fields
Geo
+3 fields
RequestSize
+2 fields
TraceContext
+2 fields
wide event·4 fields
{
method:"POST",
path:"/api/checkout",
status:200,
duration:234,
userAgent.browser:"chrome 142",+UserAgent
userAgent.os:"macOS 26",+UserAgent
userAgent.device:"desktop",+UserAgent
geo.country:"FR",+Geo
geo.city:"Paris",+Geo
geo.region:"Île-de-France",+Geo
request.size:1248,+RequestSize
response.size:8412,+RequestSize
trace.traceId:"4bf92f3577b34da6a3ce…",+TraceContext
trace.spanId:"00f067aa0ba902b7",+TraceContext
}
base fields4
enriched fields+0
app code touched0 lines

Add a custom evlog enricher

Canonical guide

Full reference at Custom enrichers:

  • defineEnricher() API
  • Reading request headers
  • Async enrichers
  • Combining built-in + custom

This page exists in the build-on-top section as a pointer — same content, classified by axis.

When to reach for plugins instead

If your feature mixes enrichment with other hooks (e.g. enrich + tail-sample + side-effect on drain), use a plugin instead — one cohesive object covering several lifecycle points.