@@ -288,12 +288,74 @@ async fn handle_request(
288288 if let Some ( ref metrics) = dashboard_metrics {
289289 let duration_ms = start. elapsed ( ) . as_millis ( ) as u64 ;
290290 let is_error = response. status ( ) . is_server_error ( ) ;
291- metrics. record_request ( & path, duration_ms, exec_path, is_error) ;
291+ let metrics_path = normalize_metrics_path ( & path) ;
292+ metrics. record_request ( & metrics_path, duration_ms, exec_path, is_error) ;
292293 }
293294
294295 response
295296}
296297
298+ fn normalize_metrics_path ( path : & str ) -> String {
299+ const MAX_SEGMENTS : usize = 16 ;
300+ const MAX_SEGMENT_LEN : usize = 64 ;
301+
302+ if path. is_empty ( ) || path == "/" {
303+ return "/" . to_string ( ) ;
304+ }
305+
306+ let mut normalized = String :: with_capacity ( path. len ( ) . min ( 256 ) ) ;
307+ normalized. push ( '/' ) ;
308+
309+ let mut first = true ;
310+ for ( idx, segment) in path. split ( '/' ) . filter ( |s| !s. is_empty ( ) ) . enumerate ( ) {
311+ if idx >= MAX_SEGMENTS {
312+ if !first {
313+ normalized. push ( '/' ) ;
314+ }
315+ normalized. push_str ( "{truncated}" ) ;
316+ break ;
317+ }
318+
319+ if !first {
320+ normalized. push ( '/' ) ;
321+ }
322+ first = false ;
323+
324+ if is_dynamic_metrics_segment ( segment) || segment. len ( ) > MAX_SEGMENT_LEN {
325+ normalized. push_str ( "{param}" ) ;
326+ } else {
327+ normalized. push_str ( segment) ;
328+ }
329+ }
330+
331+ if normalized. len ( ) > 256 {
332+ normalized. truncate ( 256 ) ;
333+ }
334+
335+ normalized
336+ }
337+
338+ fn is_dynamic_metrics_segment ( segment : & str ) -> bool {
339+ if segment. is_empty ( ) {
340+ return false ;
341+ }
342+
343+ if segment. bytes ( ) . all ( |b| b. is_ascii_digit ( ) ) {
344+ return true ;
345+ }
346+
347+ // Heuristic for UUID/opaque identifier segments:
348+ // long hex-ish strings, optionally containing dashes.
349+ let hex_or_dash = segment
350+ . bytes ( )
351+ . all ( |b| b. is_ascii_hexdigit ( ) || b == b'-' ) ;
352+ if hex_or_dash && segment. len ( ) >= 16 {
353+ return true ;
354+ }
355+
356+ false
357+ }
358+
297359/// Direct routing without middleware chain - maximum performance path
298360#[ inline]
299361async fn route_request_direct (
0 commit comments