From b1866617c67ba890ce5b7d7065a89e891ffc01a5 Mon Sep 17 00:00:00 2001 From: William Oldham Date: Sat, 29 Aug 2026 19:14:48 +0100 Subject: [PATCH] fix(logs): don't spam the logs with 404 and add better logging for server errors --- server/plugins/log-errors.ts | 38 ++++++++++++++++++++++++++++++++++++ src/error.vue | 8 +++++--- 2 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 server/plugins/log-errors.ts diff --git a/server/plugins/log-errors.ts b/server/plugins/log-errors.ts new file mode 100644 index 0000000..fb5a969 --- /dev/null +++ b/server/plugins/log-errors.ts @@ -0,0 +1,38 @@ +import type { H3Error } from 'h3'; + +/** + * Logs server-side errors in a compact, readable format. + * + * Nitro fires the `error` hook for every request error, whatever the source + * (API routes, server routes/middleware, SSR rendering). It also fires for + * expected HTTP errors like 404s, so anything below a 500 is ignored here. + */ +export default defineNitroPlugin((nitroApp) => { + nitroApp.hooks.hook('error', (err, { event }) => { + const error = err as Partial; + const statusCode = error.statusCode ?? 500; + + // Skip expected client errors (404, 401, 422, redirects, ...). + if (statusCode < 500) { + return; + } + + const where = event ? `${event.method} ${event.path}` : 'non-request'; + const tag = error.unhandled ? '[unhandled]' : ''; + + console.error(`[server error] ${tag} ${where} -> ${statusCode} ${err.message}`); + + if (error.data !== undefined) { + console.error(' data:', error.data); + } + + if (err.cause) { + console.error(' cause:', err.cause); + } + + if (err.stack) { + // Drop the first line (already printed above as the message). + console.error(err.stack.split('\n').slice(1).join('\n')); + } + }); +}); diff --git a/src/error.vue b/src/error.vue index cfb6106..f480546 100644 --- a/src/error.vue +++ b/src/error.vue @@ -3,9 +3,11 @@ import { watchImmediate } from '@vueuse/core'; import DefaultLayout from './layouts/default.vue'; const { error } = defineProps<{ error: any }>(); -watchImmediate([error], () => { - console.error(error); -}); +if (import.meta.client) { + watchImmediate([() => error], () => { + console.error(error); + }); +}