Enrichers

Custom Enrichers

Write custom enrichers to add derived context to your wide events. Add deployment metadata, tenant IDs, feature flags, or any computed data.

Write custom enrichers to add any derived context to your wide events. An enricher is a function that receives an EnrichContext and mutates the event.

Write a custom evlog enricher

Basic Example

Add deployment metadata to every event. The enricher is the same function everywhere — only the wiring step differs per framework.

// server/plugins/evlog-enrich.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:enrich', (ctx) => {
    ctx.event.deploymentId = process.env.DEPLOYMENT_ID
    ctx.event.deployedBy = process.env.DEPLOYED_BY
  })
})

EnrichContext

The evlog:enrich hook receives an EnrichContext:

enrich-context.ts
interface EnrichContext {
  /** The emitted wide event (mutable) */
  event: WideEvent
  /** Request metadata */
  request?: {
    method?: string
    path?: string
    requestId?: string
  }
  /** Safe HTTP request headers (sensitive headers filtered out) */
  headers?: Record<string, string>
  /** Response metadata */
  response?: {
    status?: number
    headers?: Record<string, string>
  }
}

Factory Pattern

For reusable enrichers with options, use the factory pattern (same as built-in enrichers):

server/utils/enrichers.ts
import type { EnrichContext } from 'evlog'

interface TenantEnricherOptions {
  headerName?: string
  overwrite?: boolean
}

export function createTenantEnricher(options: TenantEnricherOptions = {}) {
  const headerName = options.headerName ?? 'x-tenant-id'

  return (ctx: EnrichContext) => {
    if (!options.overwrite && ctx.event.tenantId !== undefined) return

    const tenantId = ctx.headers?.[headerName]
    if (tenantId) {
      ctx.event.tenantId = tenantId
    }
  }
}
// server/plugins/evlog-enrich.ts
import { createTenantEnricher } from '~/server/utils/enrichers'

export default defineNitroPlugin((nitroApp) => {
  const enrichTenant = createTenantEnricher({ headerName: 'x-org-id' })

  nitroApp.hooks.hook('evlog:enrich', enrichTenant)
})

Combining with Built-in Enrichers

Custom and built-in enrichers compose freely — they're all just (ctx: EnrichContext) => void functions:

enrichers.ts
import type { EnrichContext } from 'evlog'
import { createUserAgentEnricher, createGeoEnricher } from 'evlog/enrichers'

const region = (ctx: EnrichContext) => {
  ctx.event.region = process.env.FLY_REGION ?? process.env.AWS_REGION
  ctx.event.instance = process.env.FLY_ALLOC_ID ?? process.env.HOSTNAME
}

export const enrichers = [
  createUserAgentEnricher(),
  createGeoEnricher(),
  region,
]
// Wire `enrichers` to your framework — see the Basic Example above for tabs per framework.

More Examples

Each example below is a plain (ctx: EnrichContext) => void function — wire it the same way as the Basic Example, regardless of framework.

Feature Flags

enricher-feature-flags.ts
import type { EnrichContext } from 'evlog'

export const featureFlags = (ctx: EnrichContext) => {
  ctx.event.featureFlags = {
    newCheckout: isEnabled('new-checkout'),
    betaApi: isEnabled('beta-api'),
  }
}

Response Time Classification

enricher-perf-tier.ts
import type { EnrichContext } from 'evlog'

export const performanceTier = (ctx: EnrichContext) => {
  const duration = ctx.event.duration as number | undefined
  if (duration === undefined) return

  if (duration < 100) ctx.event.performanceTier = 'fast'
  else if (duration < 500) ctx.event.performanceTier = 'normal'
  else if (duration < 2000) ctx.event.performanceTier = 'slow'
  else ctx.event.performanceTier = 'critical'
}

Next Steps