1- import type { Handle } from '@sveltejs/kit' ;
1+ import type { Handle , HandleServerError } from '@sveltejs/kit' ;
22import { resolveSession } from '$lib/server/auth/session' ;
33import { normalizeLocale } from '$lib/i18n' ;
4+ import { logger } from '$lib/server/logger' ;
5+
6+ const REQUEST_ID_MAX = 64 ;
7+
8+ // Use a client-supplied X-Request-Id when present (sanitised), otherwise mint
9+ // one. Keeps correlation across a proxy without trusting arbitrary input.
10+ function resolveRequestId ( header : string | null ) : string {
11+ if ( header ) {
12+ const cleaned = header . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '' ) . slice ( 0 , REQUEST_ID_MAX ) ;
13+ if ( cleaned ) return cleaned ;
14+ }
15+ return crypto . randomUUID ( ) ;
16+ }
417
518export const handle : Handle = async ( { event, resolve } ) => {
19+ const requestId = resolveRequestId ( event . request . headers . get ( 'x-request-id' ) ) ;
20+ event . locals . requestId = requestId ;
21+ event . locals . log = logger . child ( { requestId } ) ;
22+
623 const token = event . cookies . get ( 'pg_session' ) ;
724
825 if ( token ) {
@@ -19,12 +36,39 @@ export const handle: Handle = async ({ event, resolve }) => {
1936 // per-request inside `+layout.ts`.
2037 event . locals . locale = normalizeLocale ( event . request . headers . get ( 'accept-language' ) ) ;
2138
39+ const start = performance . now ( ) ;
2240 const response = await resolve ( event ) ;
41+ const durationMs = Math . round ( performance . now ( ) - start ) ;
2342
43+ response . headers . set ( 'X-Request-Id' , requestId ) ;
2444 response . headers . set ( 'X-Frame-Options' , 'DENY' ) ;
2545 response . headers . set ( 'X-Content-Type-Options' , 'nosniff' ) ;
2646 response . headers . set ( 'Referrer-Policy' , 'strict-origin-when-cross-origin' ) ;
2747 response . headers . set ( 'Permissions-Policy' , 'camera=(), microphone=(), geolocation=()' ) ;
2848
49+ // Skip the noisy liveness/readiness probes.
50+ const path = event . url . pathname ;
51+ if ( path !== '/health' && path !== '/readyz' ) {
52+ event . locals . log . info (
53+ { method : event . request . method , path, status : response . status , durationMs } ,
54+ 'request'
55+ ) ;
56+ }
57+
2958 return response ;
3059} ;
60+
61+ export const handleError : HandleServerError = ( { error, event, status, message } ) => {
62+ logger . error (
63+ {
64+ requestId : event . locals . requestId ,
65+ method : event . request . method ,
66+ path : event . url . pathname ,
67+ status,
68+ err : error instanceof Error ? { message : error . message , stack : error . stack } : String ( error )
69+ } ,
70+ 'unhandled error'
71+ ) ;
72+
73+ return { message } ;
74+ } ;
0 commit comments