first commit
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
# `metrics.ts` — detector-chain OTel instruments
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## 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
|
||||
|
||||
```ts
|
||||
import { getDetectorMetrics } from "@suaveplan/i18n-detector";
|
||||
|
||||
const m = getDetectorMetrics();
|
||||
m.matchTotal.add(1, { detector: "cookie" });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
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).
|
||||
Reference in New Issue
Block a user