11interface RequestMetrics {
22 total_requests : number ;
3+
34 total_errors : number ;
45 total_success : number ;
56 error_rate : number ;
@@ -33,16 +34,60 @@ interface MetricsSnapshot {
3334 by_path : PathMetrics ;
3435}
3536
36- const startTime = Date . now ( ) ;
37- const requestWindow : Array < { status : number ; latency : number ; timestamp : number } > = [ ] ;
38- const MAX_WINDOW_SIZE = 10000 ;
37+ interface RequestEntry {
38+ method : string ;
39+ path : string ;
40+ status : number ;
41+ latency : number ;
42+ timestamp : number ;
43+ }
44+
45+ interface MethodCounter {
46+ count : number ;
47+ errors : number ;
48+ totalLatencyMs : number ;
49+ }
3950
51+ interface PathCounter {
52+ count : number ;
53+ errors : number ;
54+ totalLatencyMs : number ;
55+ }
56+
57+ const startTime = Date . now ( ) ; const requestWindow : RequestEntry [ ] = [ ] ; const MAX_WINDOW_SIZE = 10000 ; const methodCounters : { [ method : string ] : MethodCounter } = { } ; const pathCounters : { [ path : string ] : PathCounter } = { } ;
4058export function recordRequest ( method : string , path : string , status : number , latencyMs : number ) : void {
4159 const timestamp = Date . now ( ) ;
42- requestWindow . push ( { status, latency : latencyMs , timestamp } ) ;
60+ requestWindow . push ( { method, path, status, latency : latencyMs , timestamp } ) ;
61+
62+ const methodCounter = methodCounters [ method ] ||= { count : 0 , errors : 0 , totalLatencyMs : 0 } ;
63+ methodCounter . count ++ ;
64+ methodCounter . totalLatencyMs += latencyMs ;
65+ if ( status >= 400 ) methodCounter . errors ++ ;
66+
67+ const pathCounter = pathCounters [ path ] ||= { count : 0 , errors : 0 , totalLatencyMs : 0 } ;
68+ pathCounter . count ++ ;
69+ pathCounter . totalLatencyMs += latencyMs ;
70+ if ( status >= 400 ) pathCounter . errors ++ ;
4371
4472 if ( requestWindow . length > MAX_WINDOW_SIZE ) {
45- requestWindow . shift ( ) ;
73+ const removed = requestWindow . shift ( ) ;
74+ if ( removed ) {
75+ const mc = methodCounters [ removed . method ] ;
76+ if ( mc ) {
77+ mc . count -- ;
78+ mc . totalLatencyMs -= removed . latency ;
79+ if ( removed . status >= 400 ) mc . errors -- ;
80+ if ( mc . count <= 0 ) delete methodCounters [ removed . method ] ;
81+ }
82+
83+ const pc = pathCounters [ removed . path ] ;
84+ if ( pc ) {
85+ pc . count -- ;
86+ pc . totalLatencyMs -= removed . latency ;
87+ if ( removed . status >= 400 ) pc . errors -- ;
88+ if ( pc . count <= 0 ) delete pathCounters [ removed . path ] ;
89+ }
90+ }
4691 }
4792}
4893
@@ -66,7 +111,24 @@ export function getMetrics(): MetricsSnapshot {
66111 const errorsPerMinute = recentRequests . filter ( ( r ) => r . status >= 400 ) . length ;
67112
68113 const byMethod : MethodMetrics = { } ;
114+ for ( const method of Object . keys ( methodCounters ) ) {
115+ const counter = methodCounters [ method ] ;
116+ byMethod [ method ] = {
117+ count : counter . count ,
118+ errors : counter . errors ,
119+ avg_latency_ms : counter . count > 0 ? Math . round ( ( counter . totalLatencyMs / counter . count ) * 100 ) / 100 : 0 ,
120+ } ;
121+ }
122+
69123 const byPath : PathMetrics = { } ;
124+ for ( const path of Object . keys ( pathCounters ) ) {
125+ const counter = pathCounters [ path ] ;
126+ byPath [ path ] = {
127+ count : counter . count ,
128+ errors : counter . errors ,
129+ avg_latency_ms : counter . count > 0 ? Math . round ( ( counter . totalLatencyMs / counter . count ) * 100 ) / 100 : 0 ,
130+ } ;
131+ }
70132
71133 return {
72134 timestamp : new Date ( now ) . toISOString ( ) ,
@@ -84,4 +146,4 @@ export function getMetrics(): MetricsSnapshot {
84146 by_method : byMethod ,
85147 by_path : byPath ,
86148 } ;
87- }
149+ }
0 commit comments