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,75 @@
# Pagination OTel Instrumentation
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
The OTel module provides optional OpenTelemetry instrumentation for pagination operations. It exposes three instruments — a counter for request totals, a histogram for page sizes, and a histogram for encode/decode durations — using a lazy-initialisation pattern that gracefully degrades to silent no-op stubs when `@opentelemetry/api` is absent from the runtime. This makes instrumentation completely safe in environments without an OTel exporter and avoids adding a hard peer dependency on the OTel API.
## Features
- `getPaginationInstruments()` — returns the singleton `PaginationInstruments` set, initialising from OTel API on first call.
- `resetPaginationInstruments()` — resets the cached singleton for test isolation.
- `PaginationInstruments` type — exposes `requestTotal` (counter), `pageSize` (histogram), and `encodeDecode` (histogram).
- Graceful no-op fallback — all instruments are safe to call without an exporter installed.
- Named instruments follow the OpenTelemetry semantic-conventions pattern: `pagination.<scope>.<metric>`.
## Usage: Basic Example
```typescript
import { getPaginationInstruments } from "@suaveplan/pagination/otel";
const { requestTotal, pageSize } = getPaginationInstruments();
// Record a successful cursor request
requestTotal.add(1, { strategy: "cursor", outcome: "success" });
// Record that a page returned 10 items
pageSize.record(10, { strategy: "cursor" });
```
## Usage: Advanced Example
```typescript
import { getPaginationInstruments } from "@suaveplan/pagination/otel";
async function trackedFetch(cursor: string | undefined) {
const { requestTotal, pageSize, encodeDecode } = getPaginationInstruments();
const t0 = performance.now();
try {
const result = await fetchPage(cursor);
requestTotal.add(1, { strategy: "cursor", outcome: "success" });
pageSize.record(result.items.length, { strategy: "cursor" });
encodeDecode.record(performance.now() - t0, { direction: "decode", type: "cursor" });
return result;
} catch (err) {
requestTotal.add(1, { strategy: "cursor", outcome: "error" });
throw err;
}
}
```
## API Reference
### `getPaginationInstruments(): PaginationInstruments`
Returns the singleton instrument set. On the first call, attempts to acquire a meter from `@opentelemetry/api` via dynamic `require`. If OTel is absent or has no `metrics` API, all instruments are replaced with silent stubs. The result is cached for subsequent calls.
### `resetPaginationInstruments(): void`
Resets the cached instruments to `null`, forcing re-initialisation on the next `getPaginationInstruments()` call. Intended for test isolation only — do not call in production.
### `PaginationInstruments`
| Field | Type | OTel instrument name |
|---------------|------------------|-----------------------------------------------|
| `requestTotal` | `OtelCounter` | `pagination.request.total` |
| `pageSize` | `OtelHistogram` | `pagination.page.size` |
| `encodeDecode` | `OtelHistogram` | `pagination.encode_decode.duration_ms` |
Instruments support an optional `attributes` record (e.g., `{ strategy: "cursor", outcome: "success" }`).
## Implementation Notes
Dynamic `require` is used (instead of a static import) so the module compiles without `@opentelemetry/api` in `package.json`. The try/catch wraps the entire OTel setup so any initialisation error (missing package, no `metrics` key) silently activates the no-op path. Callers do not need to check whether OTel is available — all four instrument methods (`add`, `record`) are always safe to call.