3.4 KiB
3.4 KiB
metrics.ts — detector-chain OTel instruments
(no submodules)
Purpose
metrics.ts owns the OTel surface for the entire
@suaveplan/i18n-detector package. It exposes lazily-built
instruments (tracer, matchTotal, durationMs,
detectedTotal, chainExhaustedTotal) plus an in-memory state
ring so spec-conformance tests can assert on exact attribute
payloads without standing up a full MeterProvider +
InMemoryMetricExporter pipeline.
All instruments are no-ops when no MeterProvider /
TracerProvider is registered (the default), so consumers pay
zero overhead unless they wire up the SDK. The cached singleton
is reset between tests via resetDetectorMetricsCache().
Features
getDetectorMetrics()— lazily builds the tracer, counters, and histograms; caches the result.recordDetectorMatch(detector, locale)— increments the aggregatei18n.detector.match_total{detector}counter AND the per-localei18n.detector.detected_total{detector,locale}counter, mirroring both events into the in-memory state ring.recordDetectorDuration(detector, durationMs)— recordsi18n.detector.duration_ms{detector}and pushes the sample into state.recordChainExhausted()— incrementsi18n.detector.chain.exhausted_totalon every sentinel return.getDetectorMetricsState()/resetDetectorMetricsState()— test-only helpers for asserting + clearing the in-memory ring.resetDetectorMetricsCache()— drops the singleton so a fresh meter can be built between tests.
Usage: Basic Example
import { getDetectorMetrics } from "@suaveplan/i18n-detector";
const m = getDetectorMetrics();
m.matchTotal.add(1, { detector: "cookie" });
Usage: Advanced Example
import {
getDetectorMetricsState,
recordDetectorMatch,
resetDetectorMetricsState,
} from "@suaveplan/i18n-detector";
beforeEach(() => resetDetectorMetricsState());
it("increments detected_total once per match", () => {
recordDetectorMatch("cookie", "fr-FR");
const state = getDetectorMetricsState();
expect(state.detectedTotal).toEqual([
{ attributes: { detector: "cookie", locale: "fr-FR" } },
]);
});
API Reference
getDetectorMetrics()— returns the cachedCachedInstrumentsobject (tracer,matchTotal,durationMs,detectedTotal,chainExhaustedTotal).recordDetectorMatch(detector, locale)— emits both aggregate and labelled counters.recordDetectorDuration(detector, ms)— emits the duration histogram.recordChainExhausted()— emits the chain-exhausted counter.getDetectorMetricsState()/resetDetectorMetricsState()/resetDetectorMetricsCache()— test plumbing.DetectorMetricSample/DetectorMetricsState— in-memory ring shape.
Implementation Notes
- The cached singleton has process-wide lifetime by design — metrics emitters are not per-chain because the chain is a thin orchestrator and detector names are globally unique.
state.detectedTotalcarries{detector, locale}attributes;state.matchTotalcarries{detector}only. Tests that need per-locale assertions readdetectedTotal; tests that only care about which detector won readmatchTotal.- All sample arrays are unbounded —
resetDetectorMetricsStateis the safety valve. Production code should never call the state-inspection helpers (they exist for spec conformance).