UNPKG

@dudousxd/nestjs-telescope

Version:

Laravel Telescope-style observability console for NestJS — core: watchers, recorder, correlation, SQLite store, headless API.

81 lines 3.25 kB
/** Defensively read the queue/status/waitMs we need off an entry's content. */ function readJob(content) { if (typeof content !== 'object' || content === null) { return { queue: 'unknown', status: null, waitMs: null }; } const record = content; const queue = typeof record.queue === 'string' && record.queue ? record.queue : 'unknown'; return { queue, status: typeof record.status === 'string' ? record.status : null, waitMs: typeof record.waitMs === 'number' ? record.waitMs : null, }; } /** Nearest-rank percentile of an ascending-sorted array. */ function percentile(sortedAscending, fraction) { if (sortedAscending.length === 0) return 0; const rank = Math.ceil(fraction * sortedAscending.length); const index = Math.min(sortedAscending.length, Math.max(1, rank)) - 1; return sortedAscending[index]; } function statsOf(values) { if (values.length === 0) return null; const sorted = [...values].sort((a, b) => a - b); const sum = sorted.reduce((acc, value) => acc + value, 0); return { avg: sum / sorted.length, p50: percentile(sorted, 0.5), p95: percentile(sorted, 0.95), max: sorted[sorted.length - 1], }; } /** Group `job` entries by queue and compute per-queue throughput, runtime/wait * percentiles, and failure rate over the [windowStart, windowEnd] window. * Non-job entries are ignored. Pure: callers fetch the entries; entries are * assumed already windowed (createdAt is not re-checked here). */ export function aggregateQueueMetrics(entries, windowStart, windowEnd) { const windowMs = Math.max(0, windowEnd.getTime() - windowStart.getTime()); const windowMinutes = windowMs / 60_000; const groups = new Map(); for (const entry of entries) { if (entry.type !== 'job') continue; const job = readJob(entry.content); let group = groups.get(job.queue); if (!group) { group = { total: 0, completed: 0, failed: 0, runtimes: [], waits: [] }; groups.set(job.queue, group); } group.total += 1; if (job.status === 'failed') group.failed += 1; else if (job.status === 'completed') group.completed += 1; // Runtime samples are status-agnostic: any job with a numeric durationMs. if (typeof entry.durationMs === 'number') group.runtimes.push(entry.durationMs); if (job.waitMs !== null) group.waits.push(job.waitMs); } const queues = [...groups.entries()] .map(([queue, group]) => ({ queue, total: group.total, completed: group.completed, failed: group.failed, failureRate: group.total > 0 ? group.failed / group.total : 0, throughputPerMinute: windowMinutes > 0 ? group.total / windowMinutes : 0, runtimeMs: statsOf(group.runtimes), waitMs: statsOf(group.waits), })) .sort((a, b) => b.total - a.total || a.queue.localeCompare(b.queue)); return { windowStart: windowStart.toISOString(), windowEnd: windowEnd.toISOString(), windowMs, queues, }; } //# sourceMappingURL=queue-metrics.js.map