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,72 @@
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## `otel` — OpenTelemetry instrument set
### Purpose
Defines and lazily caches the OpenTelemetry instrument bundle for `@suaveplan/storage-browser-localstorage`. Lazy construction matters because the global `MeterProvider` is typically installed by the application's bootstrap code AFTER the storage package has been imported — eagerly creating instruments against the no-op default provider would freeze them on the wrong meter for the lifetime of the process. The override hook (`setStorageBrowserLocalStorageMeterProvider`) lets tests install a recording provider, drive the production code path under real instrumentation, and inspect the recorded counters and histograms — no mocks required.
### Features
- Four counters (`reads_total`, `writes_total`, `deletes_total`, `errors_total`) and two histograms (`op_duration_ms`, `io_bytes`).
- All instrument names prefixed with `storage_browser_localstorage.` for unambiguous dashboards.
- `recordOp` is the single recording funnel; production code only ever calls this helper.
- Provider override + cache reset for deterministic test isolation.
- Counter routing: `get` / `getPartial` / `getWithMetadata` → reads, `set` / `setPartial` → writes, `delete` / `clear` → deletes, `error` / `aborted` → errors.
### Usage: Basic Example
```ts
import {
getInstruments,
recordOp,
} from "@suaveplan/storage-browser-localstorage/otel";
recordOp({
op: "set",
outcome: "success",
durationMs: 1.2,
bytes: 42,
direction: "write",
});
const inst = getInstruments();
console.log(typeof inst.writesTotal.add);
```
### Usage: Advanced Example
```ts
import { MeterProvider } from "@opentelemetry/sdk-metrics";
import {
recordOp,
resetInstruments,
setStorageBrowserLocalStorageMeterProvider,
} from "@suaveplan/storage-browser-localstorage/otel";
const provider = new MeterProvider();
setStorageBrowserLocalStorageMeterProvider(provider);
resetInstruments();
try {
recordOp({ op: "get", outcome: "success", durationMs: 0.5 });
// Drain `provider` here via your reader of choice.
} finally {
setStorageBrowserLocalStorageMeterProvider(undefined);
resetInstruments();
}
```
### API Reference
- `getInstruments(force?): StorageBrowserLocalStorageInstruments` — returns the cached instrument bundle, rebuilding it when `force === true`.
- `recordOp(input): void` — records a single op's duration + counter + optional io-bytes histogram.
- `setStorageBrowserLocalStorageMeterProvider(provider | undefined): void` — installs an override provider; pass `undefined` to fall back to the global.
- `resetInstruments(): void` — clears the cache so the next `getInstruments` rebuilds against the current (possibly newly installed) provider.
- Types: `StorageBrowserLocalStorageOp`, `StorageBrowserLocalStorageOpOutcome`, `StorageBrowserLocalStorageInstruments`, `RecordOpInput`.
### Implementation Notes
The `op_duration_ms` histogram records both success and failure samples — the `outcome` attribute distinguishes them, so dashboards can compute success-only p95s and failure-only error rates from a single histogram. The `io_bytes` histogram is only recorded when `bytes` AND `direction` are both supplied, so list/clear/delete operations don't pollute it with zero-byte samples. Counter routing uses a strict if/else-if cascade rather than a lookup table because the discrete set of ops is small and the cascade compiles to a tight switch in V8.