UNPKG

@gati-framework/runtime

Version:

Gati runtime execution engine for running handler-based applications

127 lines 4.01 kB
/** * @module runtime/trace-storage * @description Storage for request traces with TTL-based expiration */ /** * In-memory trace storage with TTL */ export class InMemoryTraceStorage { traces = new Map(); config; cleanupInterval; constructor(config = {}) { this.config = { maxTraces: config.maxTraces ?? 1000, ttlMs: config.ttlMs ?? 300000, // 5 minutes compression: config.compression ?? true, }; this.startCleanup(); } async storeTrace(trace) { const stored = { trace: this.config.compression ? this.compress(trace) : trace, expiresAt: Date.now() + this.config.ttlMs, compressed: this.config.compression, }; this.traces.set(trace.id, stored); this.enforceLimit(); } async getTrace(traceId) { const stored = this.traces.get(traceId); if (!stored) return null; if (Date.now() > stored.expiresAt) { this.traces.delete(traceId); return null; } return stored.compressed ? this.decompress(stored.trace) : stored.trace; } async listTraces(filter) { const now = Date.now(); const traces = []; for (const [id, stored] of this.traces.entries()) { if (now > stored.expiresAt) { this.traces.delete(id); continue; } const trace = stored.compressed ? this.decompress(stored.trace) : stored.trace; if (this.matchesFilter(trace, filter)) { traces.push(trace); } } // Apply limit if (filter?.limit) { return traces.slice(0, filter.limit); } return traces; } async deleteTrace(traceId) { return this.traces.delete(traceId); } async clear() { this.traces.clear(); } getStats() { let size = 0; for (const stored of this.traces.values()) { size += JSON.stringify(stored.trace).length; } return { count: this.traces.size, size }; } destroy() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); } this.traces.clear(); } startCleanup() { this.cleanupInterval = setInterval(() => { const now = Date.now(); for (const [id, stored] of this.traces.entries()) { if (now > stored.expiresAt) { this.traces.delete(id); } } }, 60000); // Every minute if (this.cleanupInterval.unref) { this.cleanupInterval.unref(); } } enforceLimit() { if (this.traces.size <= this.config.maxTraces) return; const entries = Array.from(this.traces.entries()); entries.sort((a, b) => a[1].expiresAt - b[1].expiresAt); const toRemove = entries.slice(0, entries.length - this.config.maxTraces); toRemove.forEach(([id]) => this.traces.delete(id)); } matchesFilter(trace, filter) { if (!filter) return true; if (filter.status && trace.status !== filter.status) return false; if (filter.path && !trace.request.path.includes(filter.path)) return false; if (filter.startTime && trace.timestamp < filter.startTime) return false; if (filter.endTime && trace.timestamp > filter.endTime) return false; return true; } compress(trace) { // Simple compression: just return as-is for now // Response interface doesn't have body property return trace; } decompress(trace) { // No actual decompression needed for our simple compression return trace; } } /** * Create a trace storage instance */ export function createTraceStorage(config) { return new InMemoryTraceStorage(config); } //# sourceMappingURL=trace-storage.js.map