Structured Logging

evlog wide events, enrichment plugins, sampling, drain pipeline, and browser transport.

Overview

Foundry uses evlog for structured logging with wide events — one log per request that accumulates context.

For detailed evlog documentation, see the evlog GitHub repository.

Server-Side Logging

In server handlers, use useLogger(event) to get a request-scoped logger:

const log = useLogger(event)

// Accumulate context throughout the handler
log.set({ lead: { email: formData.email } })
log.set({ delivery: { platform: 'discord', status: 'sent' } })

// The wide event is auto-emitted at the end of the request

For errors, use createEvlogError() with why and fix fields for AI-readable diagnostics:

throw createEvlogError({
  statusCode: 400,
  why: 'Honeypot field was filled — likely bot submission',
  fix: 'Legitimate users should not see this error. Check for browser extensions filling hidden fields.',
})

Layer Plugins

The layer ships two evlog plugins that run automatically:

evlog-enrich: Adds user agent and geo data to every request.

evlog-sampling: Signal capture events (with lead.email or delivery) are never sampled out.

Drain Pipeline

Configure a log destination in server/plugins/evlog-drain.ts. Supported: Sentry, Axiom, PostHog, Better Stack, OTLP.

See the evlog drains documentation for setup examples and configuration options.

Browser Transport

Client-side log.info() and log.error() calls are automatically sent to the server via POST /api/_evlog/ingest. They flow through the same drain pipeline as server logs.

This is enabled by default in the layer config via evlog.transport.enabled: true.

Production Sampling

Configure sampling rates in nuxt.config.ts under evlog.sampling. Signal capture events bypass sampling automatically.

nuxt.config.ts
evlog: {
  sampling: {
    rates: { info: 10, warn: 50, debug: 0 },  // Percentage
    keep: [
      { status: 400 },       // Always keep 4xx errors
      { duration: 1000 },    // Always keep slow requests
    ],
  },
}

The layer's evlog-sampling plugin ensures signal capture events (leads, webhook deliveries) bypass sampling entirely.

See evlog sampling documentation for full configuration options.

See the Logging environment variables reference for all logging-related env vars.