Initial Commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# instruments
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Owns the rate-limit OTel instrument registry. Today the package emits
|
||||
exactly one counter — `api.rate_limit.exceeded_total{plugin, route,
|
||||
key_kind}` — which the design doc §12.4 lists as the canonical
|
||||
rate-limit denial signal. The factory wraps the OTel global
|
||||
meter-provider lookup so production gets real export when an SDK is
|
||||
installed and tests get a no-op when one is not.
|
||||
|
||||
## Features
|
||||
|
||||
- `RATE_LIMIT_COUNTER_NAMES` frozen tuple — drives both factory
|
||||
construction AND the conformance test that asserts each name is
|
||||
present on the returned bundle.
|
||||
- `createRateLimitInstruments({ meterProvider? })` — explicit override
|
||||
precedence beats the global, so the middleware tests can assert
|
||||
exact `.add` call counts against an injected fake provider.
|
||||
- Returned object is `Object.freeze`-d so accidental mutation surfaces
|
||||
immediately rather than silently breaking downstream label
|
||||
invariants.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { createRateLimitInstruments } from "@suaveplan/api-rate-limit/instruments";
|
||||
|
||||
const instruments = createRateLimitInstruments();
|
||||
instruments.counters["api.rate_limit.exceeded_total"].add(1, {
|
||||
plugin: "billing",
|
||||
route: "POST /invoices",
|
||||
key_kind: "principal",
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Wire a captured meter-provider to assert exactly-once denial
|
||||
increments under a deterministic clock:
|
||||
|
||||
```ts
|
||||
import type { MeterProvider } from "@opentelemetry/api";
|
||||
import { createRateLimitInstruments } from "@suaveplan/api-rate-limit/instruments";
|
||||
|
||||
declare const fakeProvider: MeterProvider; // captures every .add() call
|
||||
const instruments = createRateLimitInstruments({ meterProvider: fakeProvider });
|
||||
// drive the middleware against the fake clock, then assert
|
||||
// fakeProvider's recorded .add() invocations match the denial transitions
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `const RATE_LIMIT_COUNTER_NAMES: readonly ["api.rate_limit.exceeded_total"]`
|
||||
- `type RateLimitCounterName = (typeof RATE_LIMIT_COUNTER_NAMES)[number]`
|
||||
- `interface RateLimitInstruments { counters: Readonly<Record<RateLimitCounterName, Counter>> }`
|
||||
- `interface RateLimitInstrumentsConfig { meterProvider?: MeterProvider }`
|
||||
- `function createRateLimitInstruments(config?: RateLimitInstrumentsConfig): RateLimitInstruments`
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The package deliberately does NOT install an SDK — that's an operator
|
||||
concern. When no SDK is registered, the OTel no-op meter-provider
|
||||
returns no-op counters whose `.add()` is a function-call no-op, so
|
||||
this module is hot-path safe even with default config. The host's
|
||||
own `HostInstruments` already declares the same counter name; both
|
||||
emit, both reach the same exporter — OTel aggregates correctly because
|
||||
the meter name is the only difference and the SDK merges series by
|
||||
name + label-set.
|
||||
Reference in New Issue
Block a user