Files
suaveplan/packages/foundation/timing/cron/src/otel/cron-otel.ts
2026-05-27 23:47:53 +01:00

94 lines
2.8 KiB
TypeScript

import { type Counter, type Histogram, metrics } from "@opentelemetry/api";
const METER_NAME = "@suaveplan/cron";
let _parseHistogram: Histogram | undefined;
let _nextRunHistogram: Histogram | undefined;
let _parseCounter: Counter | undefined;
let _nextRunCounter: Counter | undefined;
let _dstTransitionCounter: Counter | undefined;
function getParseHistogram(): Histogram {
if (!_parseHistogram) {
_parseHistogram = metrics
.getMeter(METER_NAME)
.createHistogram("cron.parse.duration_ms", {
description: "Cron expression parse duration in milliseconds",
unit: "ms",
});
}
return _parseHistogram;
}
function getNextRunHistogram(): Histogram {
if (!_nextRunHistogram) {
_nextRunHistogram = metrics
.getMeter(METER_NAME)
.createHistogram("cron.next_run.duration_ms", {
description: "Cron next-run search duration in milliseconds",
unit: "ms",
});
}
return _nextRunHistogram;
}
function getParseCounter(): Counter {
if (!_parseCounter) {
_parseCounter = metrics
.getMeter(METER_NAME)
.createCounter("cron.parse_total", {
description: "Cron parse invocations by outcome",
});
}
return _parseCounter;
}
function getNextRunCounter(): Counter {
if (!_nextRunCounter) {
_nextRunCounter = metrics
.getMeter(METER_NAME)
.createCounter("cron.next_run_total", {
description: "Cron next-run invocations by outcome and tz",
});
}
return _nextRunCounter;
}
function getDstTransitionCounter(): Counter {
if (!_dstTransitionCounter) {
_dstTransitionCounter = metrics
.getMeter(METER_NAME)
.createCounter("cron.dst_transition_total", {
description: "DST transitions crossed during next-run search",
});
}
return _dstTransitionCounter;
}
/** Outcome attribute is a closed enum so cardinality is bounded. */
export type Outcome = "success" | "error";
/** Record a parse-duration sample and increment the parse counter. */
export function recordParse(durationMs: number, outcome: Outcome): void {
getParseHistogram().record(durationMs, { outcome });
getParseCounter().add(1, { outcome });
}
/** Record a next-run-duration sample and increment the next-run counter. */
export function recordNextRun(
durationMs: number,
outcome: Outcome,
tz: string
): void {
getNextRunHistogram().record(durationMs, { outcome, tz });
getNextRunCounter().add(1, { outcome, tz });
}
/** Increment the DST-transition counter. `kind` is a closed enum. */
export function recordDstTransition(
kind: "spring-forward" | "fall-back",
tz: string
): void {
getDstTransitionCounter().add(1, { kind, tz });
}