Spaces:
Runtime error
Runtime error
| /** | |
| * Request Telemetry β FASE-09 E2E Hardening (T-45) | |
| * | |
| * Measures 7 phases of a request lifecycle and stores timings | |
| * for percentile calculations and monitoring. | |
| * | |
| * Phases: parse β validate β policy β resolve β connect β stream β finalize | |
| * | |
| * @module shared/utils/requestTelemetry | |
| */ | |
| interface PhaseTiming { | |
| phase: string; | |
| startMs: number; | |
| endMs: number; | |
| durationMs: number; | |
| [key: string]: unknown; | |
| } | |
| const PHASES = ["parse", "validate", "policy", "resolve", "connect", "stream", "finalize"] as const; | |
| interface TelemetrySummary { | |
| requestId: string; | |
| totalMs: number; | |
| phases: PhaseTiming[]; | |
| recordedAt?: number; | |
| } | |
| export class RequestTelemetry { | |
| requestId: string; | |
| startTime: number; | |
| phases: PhaseTiming[]; | |
| private _currentPhase: string | null; | |
| private _phaseStart: number | null; | |
| constructor(requestId: string) { | |
| this.requestId = requestId; | |
| this.startTime = Date.now(); | |
| this.phases = []; | |
| this._currentPhase = null; | |
| this._phaseStart = null; | |
| } | |
| /** | |
| * Begin a phase measurement. | |
| * @param {string} phase | |
| */ | |
| startPhase(phase) { | |
| if (this._currentPhase) { | |
| this.endPhase(); | |
| } | |
| this._currentPhase = phase; | |
| this._phaseStart = Date.now(); | |
| } | |
| /** | |
| * End the current phase measurement. | |
| * @param {Object} [metadata] - Additional metadata | |
| */ | |
| endPhase(metadata = {}) { | |
| if (!this._currentPhase) return; | |
| const now = Date.now(); | |
| this.phases.push({ | |
| phase: this._currentPhase, | |
| startMs: this._phaseStart - this.startTime, | |
| endMs: now - this.startTime, | |
| durationMs: now - this._phaseStart, | |
| ...metadata, | |
| }); | |
| this._currentPhase = null; | |
| this._phaseStart = null; | |
| } | |
| /** | |
| * Convenience: measure an async function as a phase. | |
| * @template T | |
| * @param {string} phase | |
| * @param {() => Promise<T>} fn | |
| * @returns {Promise<T>} | |
| */ | |
| async measure(phase, fn) { | |
| this.startPhase(phase); | |
| try { | |
| const result = await fn(); | |
| this.endPhase(); | |
| return result; | |
| } catch (error) { | |
| this.endPhase({ error: error.message }); | |
| throw error; | |
| } | |
| } | |
| /** | |
| * Get the full telemetry summary. | |
| * @returns {{ requestId: string, totalMs: number, phases: PhaseTiming[] }} | |
| */ | |
| getSummary() { | |
| // Auto-end any open phase | |
| if (this._currentPhase) { | |
| this.endPhase(); | |
| } | |
| return { | |
| requestId: this.requestId, | |
| totalMs: Date.now() - this.startTime, | |
| phases: [...this.phases], | |
| }; | |
| } | |
| } | |
| // βββ Telemetry Aggregator ββββββββββββββββββββββββ | |
| const MAX_HISTORY = 1000; | |
| /** @type {Array<{ requestId: string, totalMs: number, phases: PhaseTiming[] }>} */ | |
| const history = []; | |
| /** | |
| * Record a completed request's telemetry. | |
| * @param {RequestTelemetry} telemetry | |
| */ | |
| export function recordTelemetry(telemetry) { | |
| const summary = telemetry.getSummary(); | |
| summary.recordedAt = Date.now(); | |
| history.push(summary); | |
| while (history.length > MAX_HISTORY) { | |
| history.shift(); | |
| } | |
| } | |
| /** | |
| * Calculate percentile from sorted array. | |
| * @param {number[]} sorted | |
| * @param {number} p - Percentile (0-100) | |
| * @returns {number} | |
| */ | |
| function percentile(sorted, p) { | |
| if (sorted.length === 0) return 0; | |
| const idx = Math.ceil((p / 100) * sorted.length) - 1; | |
| return sorted[Math.max(0, idx)]; | |
| } | |
| /** | |
| * Get aggregated telemetry summary for monitoring. | |
| * @param {number} [windowMs=300000] - Time window (default 5 min) | |
| * @returns {{ count: number, p50: number, p95: number, p99: number, phaseBreakdown: Object }} | |
| */ | |
| export function getTelemetrySummary(windowMs = 300000) { | |
| const cutoff = Date.now() - windowMs; | |
| const recent = history.filter((h) => { | |
| return (h.recordedAt || 0) >= cutoff; | |
| }); | |
| if (recent.length === 0) { | |
| return { count: 0, avg: 0, p50: 0, p95: 0, p99: 0, phaseBreakdown: {} }; | |
| } | |
| const totals = recent.map((h) => h.totalMs).sort((a, b) => a - b); | |
| const avg = Math.round(totals.reduce((sum, value) => sum + value, 0) / totals.length); | |
| // Phase breakdown | |
| const phaseBreakdown = {}; | |
| for (const phase of PHASES) { | |
| const durations = recent | |
| .flatMap((h) => h.phases.filter((p) => p.phase === phase).map((p) => p.durationMs)) | |
| .sort((a, b) => a - b); | |
| if (durations.length > 0) { | |
| phaseBreakdown[phase] = { | |
| count: durations.length, | |
| p50: percentile(durations, 50), | |
| p95: percentile(durations, 95), | |
| avg: Math.round(durations.reduce((a, b) => a + b, 0) / durations.length), | |
| }; | |
| } | |
| } | |
| return { | |
| count: recent.length, | |
| avg, | |
| p50: percentile(totals, 50), | |
| p95: percentile(totals, 95), | |
| p99: percentile(totals, 99), | |
| phaseBreakdown, | |
| }; | |
| } | |
| export { PHASES }; | |