Frameworks

SvelteKit

Source Code
Automatic wide events, structured errors, drain adapters, enrichers, and tail sampling in SvelteKit applications.

The evlog/sveltekit adapter provides handle and handleError hooks that auto-create a request-scoped logger accessible via event.locals.log and useLogger(), emitting a wide event when the response completes.

Prompt
Set up evlog in my SvelteKit app.

- Install evlog: pnpm add evlog
- Add evlog/vite plugin to vite.config.ts with service name (handles auto-init, debug stripping)
- Export handle and handleError from evlog/sveltekit in hooks.server.ts
- Access the logger via event.locals.log or useLogger() in routes and services
- Use log.set() to accumulate context, throw createError() for structured errors
- Wide events are auto-emitted when each request completes

Docs: https://www.evlog.dev/frameworks/sveltekit
Adapters: https://www.evlog.dev/adapters

Quick Start

1. Install

bun add evlog

2. Add the Vite plugin

vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite'
import evlog from 'evlog/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    sveltekit(),
    evlog({
      service: 'my-api',
    }),
  ],
})

See the Vite Plugin docs for all options.

3. Create hooks

src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'

export const { handle, handleError } = createEvlogHooks()

4. Type your locals

src/app.d.ts
import type { RequestLogger } from 'evlog'

declare global {
  namespace App {
    interface Locals {
      log: RequestLogger
    }
  }
}

export {}

Wide Events

Build up context progressively through your handler. One request = one wide event:

src/routes/api/users/[id]/+server.ts
import { json } from '@sveltejs/kit'
import type { RequestHandler } from './$types'

export const GET: RequestHandler = async ({ locals, params }) => {
  locals.log.set({ user: { id: params.id } })

  const user = await db.findUser(params.id)
  locals.log.set({ user: { name: user.name, plan: user.plan } })

  const orders = await db.findOrders(params.id)
  locals.log.set({ orders: { count: orders.length, totalRevenue: sum(orders) } })

  return json({ user, orders })
}

All fields are merged into a single wide event emitted when the request completes:

Terminal output
14:58:15 INFO [my-api] GET /api/users/usr_123 200 in 12ms
  ├─ orders: count=2 totalRevenue=6298
  ├─ user: id=usr_123 name=Alice plan=pro
  └─ requestId: 4a8ff3a8-...

useLogger()

Use useLogger() to access the request-scoped logger from anywhere in the call stack without passing locals through your service layer:

src/lib/services/user.ts
import { useLogger } from 'evlog/sveltekit'

export async function findUser(id: string) {
  const log = useLogger()
  log.set({ user: { id } })

  const user = await db.findUser(id)
  log.set({ user: { name: user.name, plan: user.plan } })

  return user
}
src/routes/api/users/[id]/+server.ts
import { json } from '@sveltejs/kit'
import { findUser } from '$lib/services/user'
import type { RequestHandler } from './$types'

export const GET: RequestHandler = async ({ params }) => {
  const user = await findUser(params.id)
  return json(user)
}

Both event.locals.log and useLogger() return the same logger instance. useLogger() uses AsyncLocalStorage to propagate the logger across async boundaries.

Error Handling

Use createError for structured errors with why, fix, and link fields. The handleError hook captures thrown errors automatically:

src/routes/api/checkout/+server.ts
import { json } from '@sveltejs/kit'
import { createError } from 'evlog'
import type { RequestHandler } from './$types'

export const POST: RequestHandler = async ({ locals, request }) => {
  const { cartId } = await request.json()
  locals.log.set({ cart: { id: cartId } })

  throw createError({
    message: 'Payment failed',
    status: 402,
    why: 'Card declined by issuer',
    fix: 'Try a different payment method',
    link: 'https://docs.example.com/payments/declined',
  })
}

The error is captured and logged with both the custom context and structured error fields:

Terminal output
14:58:20 ERROR [my-api] POST /api/checkout 402 in 3ms
  ├─ error: name=EvlogError message=Payment failed status=402
  ├─ cart: id=cart_456
  └─ requestId: 880a50ac-...

Configuration

See the Configuration reference for all available options (initLogger, middleware options, sampling, silent mode, etc.).

Drain & Enrichers

Configure drain adapters and enrichers directly in the hooks options:

src/hooks.server.ts
import { createEvlogHooks } from 'evlog/sveltekit'
import { createAxiomDrain } from 'evlog/axiom'
import { createUserAgentEnricher } from 'evlog/enrichers'

const userAgent = createUserAgentEnricher()

export const { handle, handleError } = createEvlogHooks({
  drain: createAxiomDrain(),
  enrich: (ctx) => {
    userAgent(ctx)
    ctx.event.region = process.env.FLY_REGION
  },
})

Pipeline (Batching & Retry)

For production, wrap your adapter with createDrainPipeline to batch events and retry on failure:

src/hooks.server.ts
import type { DrainContext } from 'evlog'
import { createEvlogHooks } from 'evlog/sveltekit'
import { createAxiomDrain } from 'evlog/axiom'
import { createDrainPipeline } from 'evlog/pipeline'

const pipeline = createDrainPipeline<DrainContext>({
  batch: { size: 50, intervalMs: 5000 },
  retry: { maxAttempts: 3 },
})
const drain = pipeline(createAxiomDrain())

export const { handle, handleError } = createEvlogHooks({ drain })
Call drain.flush() on server shutdown to ensure all buffered events are sent. See the Pipeline docs for all options.

Tail Sampling

Use keep to force-retain specific events regardless of head sampling:

src/hooks.server.ts
export const { handle, handleError } = createEvlogHooks({
  drain: createAxiomDrain(),
  keep: (ctx) => {
    if (ctx.duration && ctx.duration > 2000) ctx.shouldKeep = true
  },
})

Route Filtering

Control which routes are logged with include and exclude patterns:

src/hooks.server.ts
export const { handle, handleError } = createEvlogHooks({
  include: ['/api/**'],
  exclude: ['/_internal/**', '/health'],
  routes: {
    '/api/auth/**': { service: 'auth-service' },
    '/api/payment/**': { service: 'payment-service' },
  },
})

Run Locally

git clone https://github.com/HugoRCD/evlog.git
cd evlog
bun install
bun run example:sveltekit

Open http://localhost:5173 to explore the interactive test UI.

Source Code

Browse the complete SvelteKit example source on GitHub.