Files
suaveplan/packages/localization/i18n/i18n-formatter-date/src/observability/observability.ts
2026-05-27 23:47:53 +01:00

79 lines
2.1 KiB
TypeScript

import { type Counter, type Histogram, metrics } from "@opentelemetry/api";
/**
* OTel instruments for the Date formatter.
*
* Histogram records per-format wall-clock duration; the counter
* increments on every cache reuse so consumers can observe cache
* effectiveness.
*
* The module retains a small in-memory state ring so spec-conformance
* tests can assert exact attribute payloads without standing up a
* full SDK exporter.
*/
export interface DateMetricSample {
readonly attributes: Record<string, string | number>;
}
export interface DateMetricsState {
readonly durationSamples: DateMetricSample[];
readonly cacheHitSamples: DateMetricSample[];
}
let cached:
| {
readonly durationMs: Histogram;
readonly cacheHit: Counter;
}
| undefined;
const state: DateMetricsState = {
durationSamples: [],
cacheHitSamples: [],
};
function ensureInstruments() {
if (cached) return cached;
const meter = metrics.getMeter("suaveplan.i18n.formatter.date");
cached = {
durationMs: meter.createHistogram("i18n.format.date.duration_ms", {
description:
"Duration in ms of DateFormatter.format / formatDateRange; labels: locale, preset.",
unit: "ms",
}),
cacheHit: meter.createCounter("i18n.format.cache_hit", {
description:
"Format-time cache reuse counter; labels: formatter (date).",
}),
};
return cached;
}
export function recordDateFormat(
locale: string,
preset: string,
durationMs: number
): void {
const ins = ensureInstruments();
const attributes = { locale, preset };
ins.durationMs.record(durationMs, attributes);
state.durationSamples.push({ attributes });
}
export function recordDateCacheHit(): void {
const ins = ensureInstruments();
const attributes = { formatter: "date" };
ins.cacheHit.add(1, attributes);
state.cacheHitSamples.push({ attributes });
}
export function getDateMetricsState(): DateMetricsState {
return state;
}
export function resetDateMetricsState(): void {
state.durationSamples.length = 0;
state.cacheHitSamples.length = 0;
}