first commit
This commit is contained in:
106
docs/ui/react/react-ui/src/observability/react-ui-metrics.md
Normal file
106
docs/ui/react/react-ui/src/observability/react-ui-metrics.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# `react-ui-metrics` — OpenTelemetry instruments for `@suaveplan/react-ui`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
Owns every OpenTelemetry counter, histogram, and span emitted from the
|
||||
`@suaveplan/react-ui` chassis. The module wraps `metrics.getMeter()` /
|
||||
`trace.getTracer()` from `@opentelemetry/api` so consumers wiring an SDK
|
||||
get full observability with zero overhead when no SDK is registered (the
|
||||
default behaviour). The instruments mirror the telemetry contract
|
||||
documented in the design proposal —
|
||||
theme switches, pack lifecycle, locale-bundle loads, themed-variants
|
||||
resolution, toast notifications, and the `react_ui.provider.mount` span.
|
||||
|
||||
## Features
|
||||
|
||||
- Lazy, cached instrument construction via `getReactUiMetrics()` so a
|
||||
single meter is shared by every production path
|
||||
- Discrete recorder helpers per signal — `recordThemeSwitch`,
|
||||
`recordPackRegister`, `recordPackUnregister`,
|
||||
`recordPackVersionMismatch`, `recordLocaleBundleLoad`,
|
||||
`recordThemedVariantsResolve`, `recordToastShown`
|
||||
- In-memory `state` ring exposed via `getReactUiMetricsState()` so
|
||||
`*.spec-conformance.test.ts` files can assert exact attribute payloads
|
||||
without standing up a full SDK pipeline
|
||||
- `startProviderMountSpan` / `startPackRegisterSpan` returning the OTel
|
||||
`Span` so call sites can `end()` once mount work completes
|
||||
- `resetReactUiMetricsState()` for test isolation between cases
|
||||
|
||||
## Usage: Basic example
|
||||
|
||||
```ts
|
||||
import { recordThemeSwitch } from "@suaveplan/react-ui/observability";
|
||||
|
||||
recordThemeSwitch({ from: "light", to: "dark", durationMs: 12.5 });
|
||||
```
|
||||
|
||||
## Usage: Advanced example
|
||||
|
||||
```ts
|
||||
import {
|
||||
getReactUiMetrics,
|
||||
getReactUiMetricsState,
|
||||
resetReactUiMetricsState,
|
||||
startProviderMountSpan,
|
||||
} from "@suaveplan/react-ui/observability";
|
||||
|
||||
// On a `<ReactUiProvider>` mount path the chassis records the span
|
||||
// from React's effect:
|
||||
const span = startProviderMountSpan({
|
||||
theme: "light",
|
||||
packCount: 2,
|
||||
locale: "en-GB",
|
||||
});
|
||||
try {
|
||||
await loadLocaleBundle();
|
||||
} finally {
|
||||
span.end();
|
||||
}
|
||||
|
||||
// In a test:
|
||||
resetReactUiMetricsState();
|
||||
const meter = getReactUiMetrics();
|
||||
expect(meter).toBeDefined();
|
||||
expect(getReactUiMetricsState().themeSwitchTotal).toHaveLength(0);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `getReactUiMetrics()` — returns the cached `{ counters, histograms,
|
||||
tracer }` bundle. Idempotent.
|
||||
- `recordThemeSwitch({ from, to, durationMs })` — increments
|
||||
`react_ui.theme.switch_total{from,to}` and records the matching
|
||||
histogram.
|
||||
- `recordPackRegister({ packName, packVersion })` — increments
|
||||
`react_ui.pack.register_total{pack.name, pack.version}`.
|
||||
- `recordPackUnregister({ packName })` — increments
|
||||
`react_ui.pack.unregister_total{pack.name}`.
|
||||
- `recordPackVersionMismatch({ packName, packVersion, reactUiVersion })` —
|
||||
increments `react_ui.pack.version_mismatch_total` with the three
|
||||
attributes.
|
||||
- `recordLocaleBundleLoad({ locale, outcome, durationMs })` — increments
|
||||
the load counter and records the duration histogram.
|
||||
- `recordThemedVariantsResolve({ component, durationMs })` — records the
|
||||
themed-variants cache-miss histogram.
|
||||
- `recordToastShown({ kind })` — increments `react_ui.toast.shown_total`.
|
||||
- `startProviderMountSpan({ theme, packCount, locale })` /
|
||||
`startPackRegisterSpan({ packName })` — open OTel spans; the caller
|
||||
owns `span.end()`.
|
||||
- `getReactUiMetricsState()` — snapshot of the in-memory ring.
|
||||
- `resetReactUiMetricsState()` — clears the ring and forces the cached
|
||||
instruments to rebuild on next access.
|
||||
|
||||
## Implementation notes
|
||||
|
||||
The instruments are constructed lazily inside `getReactUiMetrics()` so
|
||||
no work happens at import time. The recorder helpers double-write
|
||||
through the OTel API (so a wired SDK collects samples) AND into a local
|
||||
state ring (so unit tests can assert payloads without SDK plumbing).
|
||||
The state ring lives in a module-level `const` and is cleared by
|
||||
`resetReactUiMetricsState()`; every test that exercises the recorders
|
||||
should call the reset in its `afterEach`. The tracer is fetched once
|
||||
inside `getReactUiMetrics()` rather than per-span so a single tracer
|
||||
identity flows through every span the package opens.
|
||||
Reference in New Issue
Block a user