Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/nuxt-nitro-config-bridge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'evlog': patch
---

Fix Nuxt `evlog` options not reaching the Nitro plugin in dev: the Nuxt module now mirrors standalone Nitro by setting `process.env.__EVLOG_CONFIG` during `nitro:config`. When `enabled` is `false`, the Nitro plugins still attach a no-op request logger so `useLogger(event)` does not throw.
2 changes: 2 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,8 @@ export default defineConfig({

The Nuxt module uses `addVitePlugin()` to add strip + source location plugins internally. Auto-imports and client init are NOT delegated (Nuxt handles those natively). This is purely additive — no breaking change.

On `nitro:config`, the module serializes `runtimeConfig.evlog` into **`process.env.__EVLOG_CONFIG`** (same bridge as standalone Nitro). The Nitro plugin resolves config with **env first**, then `useRuntimeConfig().evlog`. That env injection is required because dev workers often cannot resolve the virtual Nitro runtime-config module reliably. **Do not** set `__EVLOG_CONFIG` yourself in a Nuxt app unless you mean to override the entire `evlog` block from `nuxt.config`.

## Framework Integration

> **Creating a new framework integration?** Follow the skill at `.agents/skills/create-framework-integration/SKILL.md`. It covers all touchpoints: source code, build config, package exports, tests, example app, and all documentation updates.
Expand Down
22 changes: 20 additions & 2 deletions packages/evlog/src/nitro-v3/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,28 @@ export default definePlugin(async (nitroApp) => {
_suppressDrainWarning: true,
})

if (!isEnabled()) return

const hooks = nitroApp.hooks as unknown as Hooks

// When globally disabled, createRequestLogger returns a no-op logger — still
// attach it so handlers can call useLogger without throwing.
if (!isEnabled()) {
hooks.hook('request', (event) => {
const { pathname } = parseURL(event.req.url)
const ctx = getContext(event)
let requestIdOverride: string | undefined
if (globalThis.navigator?.userAgent === 'Cloudflare-Workers') {
const cfRay = event.req.headers.get('cf-ray')
if (cfRay) requestIdOverride = cfRay
}
ctx.log = createRequestLogger({
method: event.req.method,
path: pathname,
requestId: requestIdOverride || ctx.requestId as string | undefined || crypto.randomUUID(),
}, { _deferDrain: true })
})
return
}

hooks.hook('request', (event) => {
const { pathname } = parseURL(event.req.url)
const ctx = getContext(event)
Expand Down
19 changes: 18 additions & 1 deletion packages/evlog/src/nitro/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,24 @@ export default defineNitroPlugin(async (nitroApp) => {
_suppressDrainWarning: true,
})

if (!isEnabled()) return
// When globally disabled, createRequestLogger returns a no-op logger — still
// attach it so handlers can call useLogger(event) without throwing.
if (!isEnabled()) {
nitroApp.hooks.hook('request', (event) => {
const e = event as ServerEvent
let requestIdOverride: string | undefined
if (globalThis.navigator?.userAgent === 'Cloudflare-Workers') {
const cfRay = getSafeHeaders(e)?.['cf-ray']
if (cfRay) requestIdOverride = cfRay
}
e.context.log = createRequestLogger({
method: e.method,
path: e.path,
requestId: requestIdOverride || e.context.requestId || crypto.randomUUID(),
}, { _deferDrain: true })
})
return
}

nitroApp.hooks.hook('request', (event) => {
const e = event as ServerEvent
Expand Down
12 changes: 10 additions & 2 deletions packages/evlog/src/nuxt/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,22 @@ export default defineNuxtModule<ModuleOptions>({
const transportEndpoint = options.transport?.endpoint ?? '/api/_evlog/ingest'
const transportCredentials = options.transport?.credentials ?? 'same-origin'

nuxt.options.runtimeConfig.evlog = options

// Register custom error handler for proper EvlogError serialization
// Only set if not already configured to avoid overwriting user's custom handler
// Mirror standalone Nitro modules: serialize evlog options into __EVLOG_CONFIG so
// resolveEvlogConfigForNitroPlugin() picks them up in dev (Nitro worker threads
// often cannot resolve useRuntimeConfig().evlog via dynamic import reliably).
// @ts-expect-error nitro:config hook exists but is not in NuxtHooks type
nuxt.hook('nitro:config', (nitroConfig: NitroConfig) => {
nitroConfig.errorHandler = nitroConfig.errorHandler || resolver.resolve('../nitro/errorHandler')
})

nuxt.options.runtimeConfig.evlog = options
const evlogForNitro = nuxt.options.runtimeConfig.evlog ?? options
if (evlogForNitro !== undefined && typeof evlogForNitro === 'object') {
process.env.__EVLOG_CONFIG = JSON.stringify(evlogForNitro)
}
})
nuxt.options.runtimeConfig.public.evlog = {
enabled: options.enabled ?? true,
console: options.console,
Expand Down
Loading