first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
# `StorageBackend` Observability
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
This module owns the OpenTelemetry instrumentation for
`@suaveplan/i18n-backend-storage`. It ships per-call metric emitters
(`recordRead`, `recordWrite`) that the backend invokes around every
`read` / `save` operation, plus a small in-memory state ring so
spec-conformance tests can assert exact attribute payloads without
standing up a full SDK + InMemoryMetricExporter pipeline. Every
instrument is built lazily and is a no-op when no MeterProvider is
registered — consumers pay zero overhead unless they wire up the
SDK.
The metric naming follows the per-backend prefix convention agreed
in the Wave 5 design: `i18n.backend.storage.*`. Sibling backend
packages emit under their own prefixes so consumers can group
dashboard panels per backend without shared-namespace collisions.
Every counter and histogram carries a low-cardinality `layout`
attribute (`single-key-per-namespace` | `single-key-per-locale` |
`flat-keys`) so production teams can drill into per-layout latency
or miss-rate.
## Features
- Five instruments — two histograms, three counters — covering both
read and write paths.
- `layout` attribute on every aggregate so per-layout dashboards
work without parsing namespaces.
- In-memory state ring for test assertions (no exporter required).
- Lazy meter construction; idempotent
`getStorageBackendMetrics()`.
- Test-only `resetStorageBackendMetricsCache()` so suites can
rebuild the meter under a fresh provider.
## Usage: Basic Example
```ts
import {
getStorageBackendMetricsState,
resetStorageBackendMetricsState,
} from "@suaveplan/i18n-backend-storage/observability";
import { StorageBackend } from "@suaveplan/i18n-backend-storage";
import { MemoryStorage } from "@suaveplan/storage";
resetStorageBackendMetricsState();
const backend = new StorageBackend({ storage: new MemoryStorage() });
await backend.save("en", "auth", { ok: "Ok" });
await backend.read("en", "auth");
const state = getStorageBackendMetricsState();
expect(state.readTotal).toEqual([
{ attributes: { outcome: "hit", layout: "single-key-per-namespace" } },
]);
```
## Usage: Advanced Example
A read miss emits both an outcome counter AND a
`read_errors_total` counter labelled by error code. Both carry the
`layout` attribute so per-layout error rates are charted directly.
```ts
import {
getStorageBackendMetricsState,
resetStorageBackendMetricsState,
} from "@suaveplan/i18n-backend-storage/observability";
import { StorageBackend } from "@suaveplan/i18n-backend-storage";
import { MemoryStorage } from "@suaveplan/storage";
resetStorageBackendMetricsState();
const backend = new StorageBackend({
storage: new MemoryStorage(),
layout: "flat-keys",
});
await backend.read("en", "missing").catch(() => undefined);
const state = getStorageBackendMetricsState();
expect(state.readErrorsTotal).toEqual([
{
attributes: {
code: "I18N.BACKEND_LOCALE_NOT_AVAILABLE",
layout: "flat-keys",
},
},
]);
```
## API Reference
### Emitted instruments
| Instrument | Kind | Labels | Unit |
|---|---|---|---|
| `i18n.backend.storage.read_duration_ms` | Histogram | `locale`, `namespace`, `outcome`, `layout` | ms |
| `i18n.backend.storage.read_total` | Counter | `outcome` (hit \| miss \| error), `layout` | count |
| `i18n.backend.storage.read_errors_total` | Counter | `code` (I18N error code), `layout` | count |
| `i18n.backend.storage.write_duration_ms` | Histogram | `locale`, `namespace`, `outcome`, `layout` | ms |
| `i18n.backend.storage.write_total` | Counter | `outcome` (ok \| error), `layout` | count |
### Functions
- `getStorageBackendMetrics()` — returns cached instruments; builds
them on first call.
- `recordRead(locale, namespace, layout, outcome, durationMs, errorCode?)`
— emit a read sample.
- `recordWrite(locale, namespace, layout, outcome, durationMs)` —
emit a write sample.
- `getStorageBackendMetricsState()` — frozen-shape snapshot of every
emitted sample since last reset.
- `resetStorageBackendMetricsState()` — clear the in-memory state
ring (test fixture).
- `resetStorageBackendMetricsCache()` — drop the cached instruments
so a fresh `MeterProvider` can be wired (test fixture).
## Implementation Notes
- The state ring is intentionally cardinality-bounded by the
caller's test cases. It exists for assertion ergonomics, not for
production telemetry — production telemetry flows through the
registered OTel `MeterProvider`.
- Cardinality safety: aggregate counters use only `outcome` +
`layout` (each three or fewer values); per-error-code attribution
lives on a separate `read_errors_total` counter so consumers can
drop the high-cardinality counter without losing aggregate
visibility.
- Metric names are stable: every `i18n.backend.storage.*` name is
part of the package's public API contract and will not change
without a major bump.