Client Logging
Server logs tell you what happened on the backend. Client logs complete the picture — user interactions, page views, frontend errors, and performance signals that never reach the server unless you capture them.
Quick Start
evlog provides a client-side logging API that works in any browser environment:
import { initLog, log } from 'evlog/client'
export default defineNuxtPlugin(() => {
initLog({ service: 'web' })
log.info({ action: 'app_init', path: window.location.pathname })
})
'use client'
import { useEffect } from 'react'
import { initLog, log } from 'evlog/client'
export function LogProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
initLog({ service: 'web' })
log.info({ action: 'app_init', path: window.location.pathname })
}, [])
return <>{children}</>
}
import { initLog, log } from 'evlog/client'
initLog({ service: 'web' })
log.info({ action: 'app_init', path: window.location.pathname })
The log object works anywhere in your client code — components, composables, event handlers.
Logging API
Wide Events
Pass an object to capture structured context, just like server-side log.set():
log.info({ action: 'page_view', path: '/products', referrer: document.referrer })
log.warn({ action: 'slow_load', component: 'ProductList', duration: 3200 })
log.error({ action: 'fetch_failed', endpoint: '/api/cart', status: 500 })
Tagged Logs
Pass a tag and message for quick, readable logs:
log.info('auth', 'User logged in')
log.warn('perf', 'Image lazy-load took 4s')
log.error('payment', 'Stripe checkout failed')
log.debug('router', 'Navigated to /checkout')
Console Output
In the browser console, logs render with colors and grouping:
[web] info { action: 'page_view', path: '/products' }
[auth] User logged in
[perf] Image lazy-load took 4s
Identity Context
Track which user generated a log with setIdentity():
import { setIdentity, clearIdentity, log } from 'evlog/client'
// After login
setIdentity({ userId: 'usr_123', plan: 'pro' })
log.info({ action: 'dashboard_view' })
// → { userId: 'usr_123', plan: 'pro', action: 'dashboard_view', ... }
// After logout
clearIdentity()
Identity fields are automatically merged into every log event until cleared. This lets you correlate browser events to specific users in your observability tools.
Configuration
initLog() accepts the following options:
| Option | Default | Description |
|---|---|---|
enabled | true | Enable or disable all client logging |
console | true | Output logs to the browser console |
pretty | true | Use colored, formatted console output |
service | 'client' | Service name included in every log event |
transport | — | Send logs to a server endpoint (see below) |
initLog({
enabled: true,
console: true,
pretty: true,
service: 'web',
transport: {
enabled: true,
endpoint: '/api/_evlog/ingest',
},
})
Sending Logs to the Server
By default, client logs only appear in the browser console. To persist them, you have two options:
Built-in Transport
The simplest approach — enable the built-in transport in initLog(). Each log is sent individually via fetch with keepalive: true. Good for low-volume apps.
import { initLog } from 'evlog/client'
export default defineNuxtPlugin(() => {
initLog({
service: 'web',
transport: {
enabled: true,
endpoint: '/api/_evlog/ingest', // default
},
})
})
import { initLog } from 'evlog/client'
initLog({
service: 'web',
transport: {
enabled: true,
endpoint: '/api/_evlog/ingest',
},
})
Browser Drain Pipeline
For higher volume or when you need batching, retries, and page-exit flushing, use the browser drain. This works with any frontend — no framework dependency.
import { initLogger, log } from 'evlog'
import { createBrowserLogDrain } from 'evlog/browser'
export default defineNuxtPlugin(() => {
const drain = createBrowserLogDrain({
drain: { endpoint: '/api/_evlog/ingest' },
pipeline: {
batch: { size: 25, intervalMs: 2000 },
retry: { maxAttempts: 2 },
},
})
initLogger({ drain })
log.info({ action: 'app_init' })
})
import { initLogger, log } from 'evlog'
import { createBrowserLogDrain } from 'evlog/browser'
const drain = createBrowserLogDrain({
drain: { endpoint: 'https://logs.example.com/v1/ingest' },
pipeline: {
batch: { size: 25, intervalMs: 2000 },
retry: { maxAttempts: 2 },
},
})
initLogger({ drain })
log.info({ action: 'app_init' })
The browser drain automatically:
- Batches events by size and time interval
- Retries failed sends with exponential backoff
- Flushes buffered events via
sendBeaconwhen the page becomes hidden (tab switch, navigation, close)
Next Steps
- Browser Drain — Batching, retry, and sendBeacon fallback
- Pipeline — Advanced pipeline configuration
- Structured Errors — Surface client errors with actionable context
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.
Overview
Send your logs to external services with evlog adapters. Built-in support for popular observability platforms and custom destinations.