feat: Monitor grath zoom
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Monitor, MonitorStatus, Rollup } from "@/lib/api";
|
||||
import { Monitor, MonitorSample, MonitorStatus, Rollup } from "@/lib/api";
|
||||
|
||||
/*
|
||||
* Shared vocabulary for the monitors screens.
|
||||
@@ -109,8 +109,10 @@ export function formatDuration(fromIso: string, toIso?: string): string {
|
||||
/* ------------------------------------------------------------------- tape */
|
||||
|
||||
export interface Slot {
|
||||
/** Start of the hour this slot covers. */
|
||||
/** Start of the period this slot covers. */
|
||||
at: Date;
|
||||
/** Length of that period. An hour for a rollup slot, less for a sample bucket. */
|
||||
spanMs: number;
|
||||
/** Percentage of checks that passed, or null when no check ran. */
|
||||
pct: number | null;
|
||||
checks: number;
|
||||
@@ -141,6 +143,7 @@ export function buildSlots(rollups: Rollup[], hours = 48): Slot[] {
|
||||
const r = byHour.get(at.getTime());
|
||||
slots.push({
|
||||
at,
|
||||
spanMs: 3600_000,
|
||||
checks: r?.checks ?? 0,
|
||||
pct: r && r.checks > 0 ? (r.up_count / r.checks) * 100 : null,
|
||||
latency: r && r.checks > 0 ? r.sum_latency / r.checks : null,
|
||||
@@ -149,6 +152,41 @@ export function buildSlots(rollups: Rollup[], hours = 48): Slot[] {
|
||||
return slots;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same tape, bucketed from individual check results rather than hourly
|
||||
* rollups — this is what the sub-hour ranges are drawn from, because an hourly
|
||||
* rollup cannot say anything about a window shorter than an hour.
|
||||
*
|
||||
* Buckets are built from the clock like buildSlots, for the same reason: a
|
||||
* window with no checks in it has to read as a gap and not shorten the tape.
|
||||
* `endAt` is passed rather than read from the clock so every series on one
|
||||
* screen shares an edge.
|
||||
*/
|
||||
export function buildSampleSlots(samples: MonitorSample[], windowMs: number, bucketMs: number, endAt = Date.now()): Slot[] {
|
||||
const count = Math.max(Math.round(windowMs / bucketMs), 1);
|
||||
const end = Math.floor(endAt / bucketMs) * bucketMs + bucketMs;
|
||||
const start = end - count * bucketMs;
|
||||
|
||||
const totals = Array.from({ length: count }, () => ({ checks: 0, up: 0, latency: 0 }));
|
||||
for (const sample of samples) {
|
||||
const t = new Date(sample.at).getTime();
|
||||
if (t < start || t >= end) continue;
|
||||
const bucket = totals[Math.floor((t - start) / bucketMs)];
|
||||
if (!bucket) continue;
|
||||
bucket.checks += 1;
|
||||
if (sample.up) bucket.up += 1;
|
||||
bucket.latency += sample.latency_ms;
|
||||
}
|
||||
|
||||
return totals.map((bucket, i) => ({
|
||||
at: new Date(start + i * bucketMs),
|
||||
spanMs: bucketMs,
|
||||
checks: bucket.checks,
|
||||
pct: bucket.checks > 0 ? (bucket.up / bucket.checks) * 100 : null,
|
||||
latency: bucket.checks > 0 ? bucket.latency / bucket.checks : null,
|
||||
}));
|
||||
}
|
||||
|
||||
function slotColor(s: Slot): string {
|
||||
if (s.pct === null) return "bg-border-soft";
|
||||
if (s.pct >= 99.5) return "bg-success";
|
||||
|
||||
Reference in New Issue
Block a user