Local debugging toolkit
The problem
You're debugging an app and you want to see what's happening on the server side without piping through console.log and remembering to remove the calls afterwards. Bonus : you want to replay what happened in the last hour, not just what's happening now.
The full code
1. Enable the stream server
export default defineNuxtConfig({
modules: ['evlog/nuxt'],
evlog: {
stream: true,
},
})
For Next.js / standalone / other frameworks, see the stream server page.
2. Add a /debug page
Drop this into app/pages/debug.vue (Nuxt) or wherever your app puts pages :
<script setup lang="ts">
import { onBeforeUnmount, onMounted, ref, shallowRef } from 'vue'
import type { WideEvent } from 'evlog'
const events = shallowRef<WideEvent[]>([])
const status = ref<'connecting' | 'connected' | 'error'>('connecting')
const search = ref('')
let es: EventSource | null = null
async function connect() {
const { url } = await $fetch<{ url: string | null }>('/api/_evlog/stream-info')
if (!url) {
status.value = 'error'
return
}
es = new EventSource(url)
es.onopen = () => { status.value = 'connected' }
es.onerror = () => { status.value = 'error' }
es.onmessage = (e) => {
const env = JSON.parse(e.data)
if (env.evlog !== '1') return
if (env.type === 'event' || env.type === 'replay') {
events.value = [env.data, ...events.value].slice(0, 500)
}
}
}
const filtered = computed(() => {
const q = search.value.trim().toLowerCase()
if (!q) return events.value
return events.value.filter(e => JSON.stringify(e).toLowerCase().includes(q))
})
onMounted(connect)
onBeforeUnmount(() => es?.close())
</script>
<template>
<div>
<header>
<input v-model="search" placeholder="filter…">
<span>{{ status }}</span>
</header>
<table>
<tr v-for="(e, i) in filtered" :key="i">
<td>{{ new Date(e.timestamp).toLocaleTimeString() }}</td>
<td>{{ e.level }}</td>
<td>{{ e.service }}</td>
<td>{{ e.action ?? e.message ?? e.path }}</td>
</tr>
</table>
</div>
</template>
3. Replay history before connecting (optional)
Combine with readFsLogs for a "what happened in the last hour" pre-fill :
import { readFile } from 'node:fs/promises'
// At app boot (server side, e.g. instrumentation):
const last = new Date(Date.now() - 60 * 60 * 1000)
// Stream historic events to a file or directly to the same EventSource consumers
// via the in-process stream's drain.
For an end-to-end script that pre-fills then switches to live, see Replay-then-live recipe.
What it gives you
- Live wide events in a browser tab while you
pnpm dev - Filter on any field (path, level, custom field)
- Survives page reloads (SSE auto-reconnects)
- No SDK, no dependency —
EventSourceis built into every browser
Where to go next
- Stream server — the full reference for the underlying mini-server
- Consumer recipes — variants in vanilla HTML, React, plus a Node CLI
- FS reader — for offline triage of historic logs without a running server
Overview
Real-world scenarios that combine several axes of evlog — bring a local debugging toolkit, multi-tenant context, compliance audit, or shared error vocabulary into your project.
Tenant-aware logging
Every wide event automatically carries the right tenant id, drawn from the request — no per-call-site setup, no risk of forgetting.