---
title: "Best Practices"
description: "Security guidelines, data sanitization, and production tips for evlog. Learn what not to log and how to protect sensitive data."
canonical_url: "https://www.evlog.dev/reference/best-practices"
---
# Best Practices

> Security guidelines, data sanitization, and production tips for evlog. Learn what not to log and how to protect sensitive data.

A wide event carries every field the request touched, which is what makes it queryable and what makes an accidental secret permanent. This page covers what to keep out, what to redact, and what production costs.

## What NOT to Log

The same accumulation that makes a wide event worth querying puts a secret in your drain the moment one reaches `log.set`. **Never log:**

<table>
<thead>
  <tr>
    <th>
      Category
    </th>
    
    <th>
      Examples
    </th>
    
    <th>
      Risk
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Credentials
    </td>
    
    <td>
      Passwords, API keys, tokens, secrets
    </td>
    
    <td>
      Account compromise
    </td>
  </tr>
  
  <tr>
    <td>
      Payment data
    </td>
    
    <td>
      Full card numbers, CVV, bank accounts
    </td>
    
    <td>
      PCI compliance violation
    </td>
  </tr>
  
  <tr>
    <td>
      Personal data (PII)
    </td>
    
    <td>
      SSN, passport numbers, driver's license
    </td>
    
    <td>
      Privacy laws (GDPR, CCPA)
    </td>
  </tr>
  
  <tr>
    <td>
      Health data
    </td>
    
    <td>
      Medical records, diagnoses
    </td>
    
    <td>
      HIPAA violation
    </td>
  </tr>
  
  <tr>
    <td>
      Authentication
    </td>
    
    <td>
      Session tokens, JWTs, refresh tokens
    </td>
    
    <td>
      Session hijacking
    </td>
  </tr>
</tbody>
</table>

<callout color="error" icon="i-lucide-shield-alert">

Logs are often accessible to your entire team and may be stored in third-party services. Treat them as semi-public.

</callout>

## Auto-Redaction

The simplest way to protect PII is to enable built-in auto-redaction:

<framework-tabs>

```typescript [Nuxt]nuxt.config.ts
evlog: {
  redact: true,
}
```

```typescript [Nitro]nitro.config.ts
import { defineConfig } from 'nitro'
import evlog from 'evlog/nitro/v3'

export default defineConfig({
  modules: [evlog({ redact: true })],
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'

export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  redact: true,
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'

export const { handle, handleError } = createEvlogHooks({ redact: true })
```

```typescript [TanStack Start]nitro.config.ts
import { defineConfig } from 'nitro'
import evlog from 'evlog/nitro/v3'

export default defineConfig({
  modules: [evlog({ redact: true })],
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'

export const middleware: Route.MiddlewareFunction[] = [
  evlog({ redact: true }),
]
```

```typescript [Hono]src/index.ts
import { evlog } from 'evlog/hono'

app.use(evlog({ redact: true }))
```

```typescript [Express]src/index.ts
import { evlog } from 'evlog/express'

app.use(evlog({ redact: true }))
```

```typescript [Fastify]src/index.ts
import { evlog } from 'evlog/fastify'

await app.register(evlog, { redact: true })
```

```typescript [Elysia]src/index.ts
import { evlog } from 'evlog/elysia'

app.use(evlog({ redact: true }))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'

EvlogModule.forRoot({ redact: true })
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'

const handler = withEvlog(new RPCHandler(router), { redact: true })
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'

export default withEvlog(handler, { redact: true })
```

```typescript [Standalone]src/index.ts
import { initLogger } from 'evlog'

initLogger({ redact: true })
```

</framework-tabs>

This masks credit cards (`****1111`), emails (`a***@***.com`), IPs, phone numbers, JWTs, Bearer tokens, and IBANs in every wide event, before console output and before any drain. See [Auto-Redaction](https://www.evlog.dev/learn/redaction) for the full configuration reference.

<callout color="success" icon="i-lucide-shield-check">

Auto-redaction is a safety net, not a replacement for careful logging. Always prefer explicit field selection and combine with `redact: true` for defense in depth.

</callout>

## Sanitize before the field is set

### Manual Field Selection

The safest approach is to explicitly select which fields to log:

<framework-tabs>

```typescript [Nuxt]server/api/user/update.post.ts
import { useLogger } from 'evlog'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  const body = await readBody(event)

  // ❌ NEVER log the entire request body
  // log.set({ body })

  // ✅ Explicitly select safe fields
  log.set({
    user: {
      id: body.id,
      email: maskEmail(body.email),
      // password: body.password ← NEVER include
    },
  })
})
```

```typescript [Nitro]routes/api/user/update.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  const body = await readBody(event)

  // ❌ NEVER log the entire request body
  // log.set({ body })

  // ✅ Explicitly select safe fields
  log.set({
    user: {
      id: body.id,
      email: maskEmail(body.email),
      // password: body.password ← NEVER include
    },
  })
})
```

```typescript [Next.js]app/api/user/update/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'

export const POST = withEvlog(async (request: Request) => {
  const log = useLogger()

  const body = await request.json()

  // ❌ NEVER log the entire request body
  // log.set({ body })

  // ✅ Explicitly select safe fields
  log.set({
    user: {
      id: body.id,
      email: maskEmail(body.email),
      // password: body.password ← NEVER include
    },
  })

  return Response.json({ ok: true })
})
```

```typescript [Hono]src/index.ts
app.post('/api/user/update', async (c) => {
  const log = c.get('log')

  const body = await c.req.json()

  // ❌ NEVER log the entire request body
  // log.set({ body })

  // ✅ Explicitly select safe fields
  log.set({
    user: {
      id: body.id,
      email: maskEmail(body.email),
      // password: body.password ← NEVER include
    },
  })

  return c.json({ ok: true })
})
```

```typescript [Express]src/index.ts
app.post('/api/user/update', async (req, res) => {
  const log = req.log

  const body = req.body

  // ❌ NEVER log the entire request body
  // log.set({ body })

  // ✅ Explicitly select safe fields
  log.set({
    user: {
      id: body.id,
      email: maskEmail(body.email),
      // password: body.password ← NEVER include
    },
  })

  res.json({ ok: true })
})
```

</framework-tabs>

### Helper Functions

Create utility functions to sanitize common data types:

<code-collapse>

```typescript [server/utils/sanitize.ts]
/** Masks email: john.doe@example.com → j***.d**@e***.com */
export function maskEmail(email: string): string {
  const [local, domain] = email.split('@')
  if (!domain) return '***'
  const [domainName, tld] = domain.split('.')
  return `${local[0]}***@${domainName[0]}***.${tld}`
}

/** Masks card number: 4242424242424242 → ****4242 */
export function maskCard(card: string): string {
  return `****${card.slice(-4)}`
}

/** Truncates long IDs for readability */
export function truncateId(id: string, length = 8): string {
  if (id.length <= length) return id
  return `${id.slice(0, length)}...`
}

/** Removes sensitive fields from an object */
export function sanitize<T extends Record<string, unknown>>(
  obj: T,
  sensitiveKeys: string[] = ['password', 'token', 'secret', 'apiKey', 'authorization']
): Partial<T> {
  const result = { ...obj }
  for (const key of sensitiveKeys) {
    if (key in result) {
      delete result[key]
    }
  }
  return result
}
```

</code-collapse>

Usage:

<framework-tabs>

```typescript [Nuxt]server/api/checkout.post.ts
import { useLogger } from 'evlog'

export default defineEventHandler(async (event) => {
  const log = useLogger(event)

  const { user, card } = await readBody(event)

  log.set({
    user: {
      id: user.id,
      email: maskEmail(user.email),
    },
    payment: {
      last4: maskCard(card.number),
      // ❌ Never: number, cvv, expiry
    },
  })
})
```

```typescript [Nitro]routes/api/checkout.post.ts
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog/nitro/v3'

export default defineHandler(async (event) => {
  const log = useLogger(event)

  const { user, card } = await readBody(event)

  log.set({
    user: {
      id: user.id,
      email: maskEmail(user.email),
    },
    payment: {
      last4: maskCard(card.number),
      // ❌ Never: number, cvv, expiry
    },
  })
})
```

```typescript [Next.js]app/api/checkout/route.ts
import { withEvlog, useLogger } from '@/lib/evlog'

export const POST = withEvlog(async (request: Request) => {
  const log = useLogger()

  const { user, card } = await request.json()

  log.set({
    user: {
      id: user.id,
      email: maskEmail(user.email),
    },
    payment: {
      last4: maskCard(card.number),
      // ❌ Never: number, cvv, expiry
    },
  })

  return Response.json({ ok: true })
})
```

```typescript [Hono]src/index.ts
app.post('/api/checkout', async (c) => {
  const log = c.get('log')

  const { user, card } = await c.req.json()

  log.set({
    user: {
      id: user.id,
      email: maskEmail(user.email),
    },
    payment: {
      last4: maskCard(card.number),
      // ❌ Never: number, cvv, expiry
    },
  })

  return c.json({ ok: true })
})
```

```typescript [Express]src/index.ts
app.post('/api/checkout', async (req, res) => {
  const log = req.log

  const { user, card } = req.body

  log.set({
    user: {
      id: user.id,
      email: maskEmail(user.email),
    },
    payment: {
      last4: maskCard(card.number),
      // ❌ Never: number, cvv, expiry
    },
  })

  res.json({ ok: true })
})
```

</framework-tabs>

### Drain Hook Filtering

As a last line of defense, filter sensitive data before sending to external services:

<framework-tabs>

```typescript [Nuxt]server/plugins/evlog.ts
const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}

export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', (ctx) => {
    // Sanitize before sending to external service
    ctx.event = deepSanitize(ctx.event) as typeof ctx.event
  })
})
```

```typescript [Nitro]server/plugins/evlog.ts
import { definePlugin } from 'nitro'
const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', (ctx) => {
    // Sanitize before sending to external service
    ctx.event = deepSanitize(ctx.event) as typeof ctx.event
  })
})
```

```typescript [Next.js]lib/evlog.ts
import { createEvlog } from 'evlog/next'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
export const { withEvlog, useLogger, log, createError } = createEvlog({
  service: 'my-app',
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
})
```

```typescript [SvelteKit]src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
export const { handle, handleError } = createEvlogHooks({
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
})
```

```typescript [TanStack Start]server/plugins/evlog.ts
import { definePlugin } from 'nitro'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}

export default definePlugin((nitroApp) => {
  nitroApp.hooks.hook('evlog:drain', (ctx) => {
    // Sanitize before sending to external service
    ctx.event = deepSanitize(ctx.event) as typeof ctx.event
  })
})
```

```typescript [React Router]app/root.tsx
import { evlog } from 'evlog/react-router'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
export const middleware: Route.MiddlewareFunction[] = [
  evlog({
      drain: (ctx) => {
          // Sanitize before sending to external service
          ctx.event = deepSanitize(ctx.event) as typeof ctx.event
        },
    }),
]
```

```typescript [Hono]src/index.ts
import { evlog } from 'evlog/hono'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
app.use(evlog({
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
}))
```

```typescript [Express]src/index.ts
import { evlog } from 'evlog/express'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
app.use(evlog({
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
}))
```

```typescript [Fastify]src/index.ts
import { evlog } from 'evlog/fastify'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
await app.register(evlog, {
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
})
```

```typescript [Elysia]src/index.ts
import { evlog } from 'evlog/elysia'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
app.use(evlog({
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
}))
```

```typescript [NestJS]src/app.module.ts
import { EvlogModule } from 'evlog/nestjs'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
EvlogModule.forRoot({
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
})
```

```typescript [oRPC]server/orpc.ts
import { withEvlog } from 'evlog/orpc'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
const handler = withEvlog(new RPCHandler(router), {
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
})
```

```typescript [Cloudflare Workers]src/worker.ts
import { withEvlog } from 'evlog/workers'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
export default withEvlog(handler, {
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
})
```

```typescript [Standalone]src/index.ts
import { initLogger } from 'evlog'

const SENSITIVE_KEYS = ['password', 'token', 'secret', 'apikey', 'authorization', 'cookie']

function deepSanitize(value: unknown): unknown {
  if (Array.isArray(value)) return value.map(deepSanitize)
  if (!value || typeof value !== 'object') return value

  const result: Record<string, unknown> = {}
  for (const [key, nested] of Object.entries(value)) {
    const name = key.toLowerCase().replace(/[-_]/g, '')
    result[key] = SENSITIVE_KEYS.some(k => name.includes(k)) ? '[REDACTED]' : deepSanitize(nested)
  }
  return result
}
initLogger({
  drain: (ctx) => {
      // Sanitize before sending to external service
      ctx.event = deepSanitize(ctx.event) as typeof ctx.event
    },
})
```

</framework-tabs>

<callout color="warning" icon="i-lucide-lightbulb">

Drain hook sanitization is a safety net, not a replacement for careful logging practices. Always sanitize at the source.

</callout>

## Production Checklist

Before deploying to production, verify:

### Logging Configuration

- [ ] Service name is set (`env.service`)
- [ ] Sampling is configured for high-traffic routes
- [ ] Log draining is set up for external service (Axiom, Loki, etc.)
- [ ] Pretty mode is disabled in production (`pretty: false`)

### Data Security

- [ ] Auto-redaction is enabled (`redact: true`)
- [ ] No passwords or secrets in logs
- [ ] No full credit card numbers (only last 4 digits)
- [ ] No API keys or tokens
- [ ] PII is masked or omitted (emails, phone numbers)
- [ ] Session tokens are not logged
- [ ] Request bodies are selectively logged (not `log.set({ body })`)

### Error Handling

- [ ] Errors use `createError()` with structured fields
- [ ] Sensitive data is not included in error messages
- [ ] Stack traces don't expose internal paths in production

## Group fields so they stay queryable

Use consistent, grouped field names across your codebase:

```typescript [services/checkout.ts]
// ✅ Good - grouped and descriptive
log.set({
  user: { id, plan, accountAge },
  cart: { items, total, currency },
  payment: { method, provider, last4 },
})

// ❌ Bad - flat and abbreviated
log.set({
  uid: '123',
  n: 3,
  t: 9999,
  pm: 'card',
})
```

### Recommended Field Structure

<table>
<thead>
  <tr>
    <th>
      Category
    </th>
    
    <th>
      Fields
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        user
      </code>
    </td>
    
    <td>
      <code>
        id
      </code>
      
      , <code>
        plan
      </code>
      
      , <code>
        role
      </code>
      
      , <code>
        accountAge
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request
      </code>
    </td>
    
    <td>
      <code>
        method
      </code>
      
      , <code>
        path
      </code>
      
      , <code>
        requestId
      </code>
      
      , <code>
        traceId
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        cart
      </code>
      
       / <code>
        order
      </code>
    </td>
    
    <td>
      <code>
        items
      </code>
      
      , <code>
        total
      </code>
      
      , <code>
        currency
      </code>
      
      , <code>
        coupon
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        payment
      </code>
    </td>
    
    <td>
      <code>
        method
      </code>
      
      , <code>
        provider
      </code>
      
      , <code>
        last4
      </code>
      
      , <code>
        status
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        outcome
      </code>
    </td>
    
    <td>
      <code>
        status
      </code>
      
      , <code>
        duration
      </code>
      
      , <code>
        error
      </code>
    </td>
  </tr>
</tbody>
</table>

## Decide what survives sampling

Volume is what you pay for, and a successful request nobody reads costs the same as the one that failed. Keep every error, thin the rest:

```typescript [nuxt.config.ts]
export default defineNuxtConfig({
  evlog: {
    sampling: {
      // Head sampling: random percentage per level
      rates: {
        info: 10,    // 10% of success logs
        warn: 50,    // 50% of warnings
        debug: 0,    // No debug logs in prod
        error: 100,  // Always keep errors
      },
      // Tail sampling: force-keep based on outcome
      keep: [
        { duration: 1000 },           // Slow requests (≥1s)
        { status: 400 },              // Client/server errors
        { path: '/api/payments/**' }, // Critical paths
      ],
    },
  },
})
```

<callout color="info" icon="i-lucide-info">

Use `$production` override to keep full logging in development while sampling in production. See [Nuxt framework guide](https://www.evlog.dev/integrate/frameworks/nuxt#sampling).

</callout>

## Next Steps

- [Auto-Redaction](https://www.evlog.dev/learn/redaction) - Built-in PII protection with smart masking
- [Wide Events](https://www.evlog.dev/learn/wide-events) - Design effective wide events
- [Structured Errors](https://www.evlog.dev/learn/structured-errors) - Error handling patterns

---

- [Adapters](https://www.evlog.dev/integrate/adapters/overview)


## Sitemap

See the full [sitemap](https://www.evlog.dev/sitemap.md) for all pages.
