monitor.counter-increase@1.0.0
impl/typescript.ts
1,603 bytes · the TypeScript implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { type MetricSample } from "./monitor_series_window.ts"; ← from monitor.series-window ^1.0.0 · built alongside by fune
import { type CounterIncrease } from "./monitor_counter_increase_types.ts";
/**
* How much a counter grew over the samples, Prometheus-style: a drop means the
* counter restarted from zero, so the value after it is all increase. No
* extrapolation to window edges, so the answer is a whole number.
*/
export function counterIncrease(samples: readonly MetricSample[]): CounterIncrease {
let increase = 0;
let resets = 0;
for (let i = 0; i < samples.length; i++) {
const s = samples[i];
if (i > 0 && s.at <= samples[i - 1].at) {
throw new RangeError(`samples must be in strictly ascending time order: ${s.at} follows ${samples[i - 1].at}`);
}
if (s.value < 0) throw new RangeError(`counter value must not be negative: ${s.value} at ${s.at}`);
if (i === 0) continue;
const prev = samples[i - 1].value;
if (s.value >= prev) {
increase += s.value - prev;
} else {
resets += 1;
increase += s.value;
}
}
if (samples.length === 0) return { increase: 0, resets: 0, ratePerSecondMilli: null, firstAt: null, lastAt: null };
const firstAt = samples[0].at;
const lastAt = samples[samples.length - 1].at;
let ratePerSecondMilli: number | null = null;
if (samples.length >= 2) {
// increase * 1000 passes 2^53 for a byte counter in the trillions: BigInt.
const n = BigInt(increase) * 1000n;
const d = BigInt(lastAt - firstAt);
ratePerSecondMilli = Number((2n * n + d) / (2n * d));
}
return { increase, resets, ratePerSecondMilli, firstAt, lastAt };
}