diff --git a/.changeset/lucky-sites-wash.md b/.changeset/lucky-sites-wash.md new file mode 100644 index 000000000000..fc4a2f5fb6bb --- /dev/null +++ b/.changeset/lucky-sites-wash.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: better server-side error logging diff --git a/packages/kit/src/runtime/server/index.js b/packages/kit/src/runtime/server/index.js index d6c27d7a3069..10052a10b49d 100644 --- a/packages/kit/src/runtime/server/index.js +++ b/packages/kit/src/runtime/server/index.js @@ -3,6 +3,7 @@ import { set_private_env, set_public_env } from '../shared-server.js'; import { options, get_hooks } from '__SERVER__/internal.js'; import { DEV } from 'esm-env'; import { filter_env } from '../../utils/env.js'; +import { format_server_error } from './utils.js'; import { set_read_implementation, set_manifest } from '__sveltekit/server'; import { set_app } from './app.js'; @@ -87,8 +88,14 @@ export class Server { handle: module.handle || (({ event, resolve }) => resolve(event)), handleError: module.handleError || - (({ status, error }) => - console.error((status === 404 && /** @type {Error} */ (error)?.message) || error)), + (({ status, error, event }) => { + const error_message = format_server_error( + status, + /** @type {Error} */ (error), + event + ); + console.error(error_message); + }), handleFetch: module.handleFetch || (({ request, fetch }) => fetch(request)), handleValidationError: module.handleValidationError || diff --git a/packages/kit/src/runtime/server/utils.js b/packages/kit/src/runtime/server/utils.js index f8f49cef00bc..91dbd1cb8f99 100644 --- a/packages/kit/src/runtime/server/utils.js +++ b/packages/kit/src/runtime/server/utils.js @@ -186,6 +186,59 @@ export function has_prerendered_path(manifest, pathname) { ); } +/** + * Formats the error into a nice message with sanitized stack trace + * @param {number} status + * @param {Error} error + * @param {import('@sveltejs/kit').RequestEvent} event + */ +export function format_server_error(status, error, event) { + const formatted_text = `\n\x1b[1;31m[${status}] ${event.request.method} ${event.url.pathname}\x1b[0m\n`; + + if (status === 404) { + return formatted_text + error.message; + } + + return formatted_text + (DEV ? clean_up_stack_trace(error) : error.stack); +} + +/** + * In dev, tidy up stack traces by making paths relative to the current project directory + * @param {string} file + */ +let relative = (file) => file; + +if (DEV) { + try { + const path = await import('node:path'); + const process = await import('node:process'); + + relative = (file) => path.relative(process.cwd(), file); + } catch { + // do nothing + } +} + +/** + * Provides a refined stack trace by excluding lines following the last occurrence of a line containing +page. +layout. or +server. + * @param {Error} error + */ +export function clean_up_stack_trace(error) { + const stack_trace = (error.stack?.split('\n') ?? []).map((line) => { + return line.replace(/\((.+)(:\d+:\d+)\)$/, (_, file, loc) => `(${relative(file)}${loc})`); + }); + + // progressive enhancement for people who haven't configured kit.files.src to something else + const last_line_from_src_code = stack_trace.findLastIndex((line) => /\(src[\\/]/.test(line)); + + if (last_line_from_src_code === -1) { + // default to the whole stack trace + return error.stack; + } + + return stack_trace.slice(0, last_line_from_src_code + 1).join('\n'); +} + /** * Returns the filename without the extension. e.g., `+page.server`, `+page`, etc. * @param {string | undefined} node_id diff --git a/packages/kit/tsconfig.json b/packages/kit/tsconfig.json index 8b927b041456..f87ec5cfe3cb 100644 --- a/packages/kit/tsconfig.json +++ b/packages/kit/tsconfig.json @@ -4,7 +4,7 @@ "checkJs": true, "noEmit": true, "strict": true, - "target": "es2022", + "target": "es2023", "module": "node16", "moduleResolution": "node16", "allowSyntheticDefaultImports": true, diff --git a/playgrounds/basic/src/routes/+page.svelte b/playgrounds/basic/src/routes/+page.svelte index 6cbe780c41be..1ce9b67c8dd5 100644 --- a/playgrounds/basic/src/routes/+page.svelte +++ b/playgrounds/basic/src/routes/+page.svelte @@ -6,6 +6,9 @@