Core Concepts

Sampling

Control log volume with two-tier sampling. Head sampling drops noise by level, tail sampling rescues critical events based on outcome. Never miss errors, slow requests, or critical paths.

At scale, logging everything gets expensive fast. Sampling lets you keep costs under control without losing visibility into what matters. evlog uses a two-tier approach: head sampling drops noise upfront, tail sampling rescues critical events after the fact.

Head Sampling

Head sampling randomly keeps a percentage of logs per level. It runs before the request completes — a coin flip at emission time.

export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    sampling: {
      rates: {
        info: 10,    // Keep 10% of info logs
        warn: 50,    // Keep 50% of warnings
        debug: 0,    // Drop all debug logs
        error: 100,  // Always keep errors (default)
      },
    },
  },
})

Each level is a percentage from 0 to 100. Levels you don't configure default to 100% (keep everything). Error defaults to 100% even when other levels are configured — you have to explicitly set error: 0 to drop errors.

Head sampling is random. A 10% rate means roughly 1 in 10 info logs are kept — not exactly 1 in 10.

Tail Sampling

Head sampling is blind — it doesn't know if a request was slow, failed, or hit a critical path. Tail sampling fixes this by evaluating after the request completes and force-keeping logs that match specific conditions.

// Works the same across all frameworks
sampling: {
  rates: { info: 10 },
  keep: [
    { status: 400 },              // HTTP status >= 400
    { duration: 1000 },           // Request took >= 1s
    { path: '/api/payments/**' }, // Critical path (glob)
  ],
}

Conditions use >= comparison for status and duration, and glob matching for path. If any condition matches, the log is kept regardless of head sampling (OR logic).

Available Conditions

ConditionTypeDescription
statusnumberKeep if HTTP status >= value (e.g., 400 catches all 4xx and 5xx)
durationnumberKeep if request duration >= value in milliseconds
pathstringKeep if request path matches glob pattern (e.g., '/api/critical/**')

How They Work Together

The two tiers complement each other:

  1. Request completes — evlog knows the status, duration, and path
  2. Tail sampling evaluates — if any keep condition matches, the log is force-kept
  3. Head sampling applies — only if tail sampling didn't force-keep, the random percentage check runs
  4. Log emits or drops — kept logs go through enrichment and draining as normal

This means a request to /api/payments/charge that returns a 500 in 2 seconds will always be logged, even if info is set to 1%. The tail conditions rescue it.

sampling: {
  rates: { info: 10 },
  keep: [
    { status: 400 },
    { duration: 1000 },
  ],
}

Custom Tail Sampling

For conditions beyond status, duration, and path, use the evlog:emit:keep hook in Nuxt/Nitro or the keep callback in other frameworks.

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:emit:keep', (ctx) => {
    if (ctx.context.user?.plan === 'enterprise') {
      ctx.shouldKeep = true
    }
  })
})

The ctx object contains:

FieldTypeDescription
statusnumber | undefinedHTTP response status
durationnumber | undefinedRequest duration in ms
pathstring | undefinedRequest path
methodstring | undefinedHTTP method
contextRecord<string, unknown>All fields set via log.set()
shouldKeepbooleanSet to true to force-keep

Production Example

A typical production configuration that balances cost and visibility:

export default defineNuxtConfig({
  modules: ['evlog/nuxt'],
  evlog: {
    env: { service: 'my-app' },
  },
  $production: {
    evlog: {
      sampling: {
        rates: {
          info: 10,
          warn: 50,
          debug: 0,
          error: 100,
        },
        keep: [
          { status: 400 },
          { duration: 1000 },
          { path: '/api/payments/**' },
          { path: '/api/auth/**' },
        ],
      },
    },
  },
})
In Nuxt, use the $production override to keep full logging in development while sampling in production. In other frameworks, use your own environment check or config system.

Next Steps