# otel _(no submodules)_ ## Purpose Owns the OpenTelemetry instrument set for `@suaveplan/storage-cdn-rackspace`: counters (`reads_total`, `writes_total`, `deletes_total`, `purges_total`, `signed_urls_total`, `errors_total`, `range_fallback_total`), histograms (`op_duration_ms`, `io_bytes`), and a tracer scoped to `@suaveplan/storage-cdn-rackspace`. Every measurement carries `provider="rackspace"` and the configured `region` so dashboards can break Rackspace traffic down by region and aggregate across providers without losing detail. ## Features - Lazy instrument construction so consumers can install a custom `MeterProvider` before the first operation runs. - `setRackspaceMeterProvider(provider)` override hook with auto-rebuild on next access. - `recordOp({ op, outcome, durationMs, region, bytes?, direction? })` emits the full observation set for one operation. - `recordRangeFallback(region)` increments the dedicated counter — surfaces origins that ignore Range. - `resetInstruments()` for test isolation. - Dedicated counters for CDN-specific operations (`purges_total`, `signed_urls_total`) so operators can monitor purge rate-limit pressure separately from storage IO. ## Usage: Basic ```ts import { recordOp } from "@suaveplan/storage-cdn-rackspace"; recordOp({ op: "purge", outcome: "ok", durationMs: 42, region: "LON" }); ``` ## Usage: Advanced ```ts import { resetInstruments, setRackspaceMeterProvider, recordOp, } from "@suaveplan/storage-cdn-rackspace"; import { MeterProvider } from "@opentelemetry/sdk-metrics"; const provider = new MeterProvider({ readers: [periodicReader] }); setRackspaceMeterProvider(provider); resetInstruments(); recordOp({ op: "signUrl", outcome: "ok", durationMs: 1, region: "LON" }); ``` ## API Reference ### `setRackspaceMeterProvider(provider?): void` Override or restore the meter provider. Calling this invalidates the cached instrument set so the next `getInstruments()` rebinds. ### `getInstruments(force?): RackspaceInstruments` Returns the cached instrument bundle (`tracer`, `meter`, every counter and histogram). `force = true` rebuilds. ### `recordOp(input): void` `input.outcome` decides which counter increments: - `"ok"` + read-ish op → `reads_total`. - `"ok"` + `set`/`setPartial` → `writes_total`. - `"ok"` + `delete`/`clear` → `deletes_total`. - `"ok"` + `purge` → `purges_total`. - `"ok"` + `signUrl` → `signed_urls_total`. - anything else → `errors_total`. Always records `op_duration_ms{op, outcome, provider, region}`. When `bytes` and `direction` are both provided, also records `io_bytes{op, direction, provider, region}`. ### `recordRangeFallback(region): void` Increments `range_fallback_total{outcome="ok", provider="rackspace", region}`. ### `resetInstruments(): void` Drops the cached bundle without changing the meter provider. ## Implementation Notes - The cache is critical for hot-path performance. `setRackspaceMeterProvider` is the one path that intentionally invalidates the cache. - `provider="rackspace"` is hard-coded — every measurement from this package belongs to Rackspace by definition. Cross-provider dashboards aggregate on the `provider` attribute, so dropping it would defeat the family-wide observability. - `region` is sourced from the resolved configuration, not the operation argument, because operations don't take a region — the adapter is bound to one region per instance. - The tracer is acquired separately from the meter; no caching is required because `trace.getTracer` is idempotent. - `range_fallback_total` carries `outcome="ok"` only — the fallback path always succeeds (just less efficiently); upstream failures during fallback surface via `errors_total`.