Enrichers

Built-in Enrichers

Reference for all built-in evlog enrichers. Parse user agents, extract geo data, measure request sizes, and capture trace context automatically.

All built-in enrichers are exported from evlog/enrichers. Each enricher is a factory function that returns an (ctx: EnrichContext) => void callback.

import {
  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

User Agent

Parse browser, OS, and device type from the User-Agent header.

Sets: event.userAgent

const enrich = createUserAgentEnricher()

Output shape:

interface UserAgentInfo {
  raw: string                                      // Original User-Agent string
  browser?: { name: string; version?: string }     // Chrome, Firefox, Safari, Edge
  os?: { name: string; version?: string }          // Windows, macOS, iOS, Android, Linux
  device?: { type: 'mobile' | 'tablet' | 'desktop' | 'bot' | 'unknown' }
}

Example output:

{
  "userAgent": {
    "raw": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/120.0.0.0",
    "browser": { "name": "Chrome", "version": "120.0.0.0" },
    "os": { "name": "macOS", "version": "10.15.7" },
    "device": { "type": "desktop" }
  }
}

Detected browsers: Edge, Chrome, Firefox, Safari (checked in order, Edge before Chrome to avoid false matches).

Detected devices: Bot (crawlers, spiders), Tablet (iPad), Mobile (iPhone, Android phones), Desktop (fallback).

Geo

Extract geographic data from platform-injected headers.

Sets: event.geo

const enrich = createGeoEnricher()

Output shape:

interface GeoInfo {
  country?: string      // ISO country code (e.g., "US", "FR")
  region?: string       // Region/state name
  regionCode?: string   // Region code
  city?: string         // City name
  latitude?: number     // Decimal latitude
  longitude?: number    // Decimal longitude
}

Supported platforms:

PlatformHeadersCoverage
Vercelx-vercel-ip-country, x-vercel-ip-country-region, x-vercel-ip-city, x-vercel-ip-latitude, x-vercel-ip-longitudeFull
Cloudflarecf-ipcountryCountry only
Cloudflare note: Only cf-ipcountry is a standard Cloudflare HTTP header. Other geo fields (city, region, latitude, etc.) are properties of request.cf, which is not exposed as headers. For full Cloudflare geo data, write a custom enricher that reads request.cf, or use a Workers middleware to copy cf properties into custom headers.

Request Size

Capture request and response payload sizes from Content-Length headers.

Sets: event.requestSize

const enrich = createRequestSizeEnricher()

Output shape:

interface RequestSizeInfo {
  requestBytes?: number    // Request Content-Length
  responseBytes?: number   // Response Content-Length
}

Example output:

{
  "requestSize": {
    "requestBytes": 1234,
    "responseBytes": 5678
  }
}
This enricher reads the Content-Length header from both the request and response. If the header is missing (e.g., for chunked transfer encoding), the corresponding field will be undefined.

Trace Context

Extract W3C trace context from the traceparent and tracestate headers.

Sets: event.traceContext, event.traceId, event.spanId

const enrich = createTraceContextEnricher()

Output shape:

interface TraceContextInfo {
  traceparent?: string   // Full traceparent header value
  tracestate?: string    // Full tracestate header value
  traceId?: string       // 32-char hex trace ID (parsed from traceparent)
  spanId?: string        // 16-char hex span ID (parsed from traceparent)
}

Example output:

{
  "traceContext": {
    "traceparent": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
    "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
    "spanId": "00f067aa0ba902b7"
  },
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "spanId": "00f067aa0ba902b7"
}

traceId and spanId are also set at the top level of the event for easy querying and correlation.

The traceparent format follows the W3C Trace Context specification: {version}-{traceId}-{spanId}-{flags}.

Full Setup Example

Use all built-in enrichers together:

server/plugins/evlog-enrich.ts
import {
  createUserAgentEnricher,
  createGeoEnricher,
  createRequestSizeEnricher,
  createTraceContextEnricher,
} from 'evlog/enrichers'

export default defineNitroPlugin((nitroApp) => {
  const enrichers = [
    createUserAgentEnricher(),
    createGeoEnricher(),
    createRequestSizeEnricher(),
    createTraceContextEnricher(),
  ]

  nitroApp.hooks.hook('evlog:enrich', (ctx) => {
    for (const enricher of enrichers) enricher(ctx)
  })
})

Next Steps

Copyright © 2026