Files
skills/docs/localization/i18n/i18n-detector/src/observability/metrics.md
2026-05-29 08:20:56 +01:00

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 aggregate i18n.detector.match_total{detector} counter AND the per-locale i18n.detector.detected_total{detector,locale} counter, mirroring both events into the in-memory state ring.
  • recordDetectorDuration(detector, durationMs) — records i18n.detector.duration_ms{detector} and pushes the sample into state.
  • recordChainExhausted() — increments i18n.detector.chain.exhausted_total on 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 cached CachedInstruments object (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.detectedTotal carries {detector, locale} attributes; state.matchTotal carries {detector} only. Tests that need per-locale assertions read detectedTotal; tests that only care about which detector won read matchTotal.
  • All sample arrays are unbounded — resetDetectorMetricsState is the safety valve. Production code should never call the state-inspection helpers (they exist for spec conformance).