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/lucky-sites-wash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': minor
---

feat: better server-side error logging
11 changes: 9 additions & 2 deletions packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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 ||
Expand Down
53 changes: 53 additions & 0 deletions packages/kit/src/runtime/server/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/kit/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"target": "es2023",
"module": "node16",
"moduleResolution": "node16",
"allowSyntheticDefaultImports": true,
Expand Down
3 changes: 3 additions & 0 deletions playgrounds/basic/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

<h1>Welcome to SvelteKit</h1>

<p><a href="images">images</a></p>
<p><a href="force-error">force error</a></p>

<h2>Todos</h2>

<h3>todo via JS</h3>
Expand Down
5 changes: 5 additions & 0 deletions playgrounds/basic/src/routes/force-error/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const actions = {
default: async () => {
throw new Error('test');
}
};
4 changes: 4 additions & 0 deletions playgrounds/basic/src/routes/force-error/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<br />
<form method="post">
<button type="submit"> Trigger server error </button>
</form>
Loading