first commit
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
# otel/instruments
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Wires the package's OpenTelemetry metrics surface and exposes a thin
|
||||
`instrument(op)` helper the adapter wraps around every CRUD method. The
|
||||
helper records one counter point (with `outcome: "success" | "error"`)
|
||||
plus one histogram observation per call, so dashboards can pivot on
|
||||
model/operation/outcome and also see the per-operation latency
|
||||
distribution.
|
||||
|
||||
## Features
|
||||
|
||||
- `better_auth.surrealdb.operations_total` counter — attributes:
|
||||
`model`, `operation`, `outcome`.
|
||||
- `better_auth.surrealdb.operation_duration_ms` histogram — attribute:
|
||||
`operation`.
|
||||
- Lazy meter resolution: the meter and instruments are constructed on
|
||||
first use (not at module load) so tests can install a real
|
||||
`MeterProvider` via `setGlobalMeterProvider` before the adapter
|
||||
begins issuing operations.
|
||||
- Universal: uses `performance.now()` when available, falls back to
|
||||
`Date.now()` when running under bundlers that strip `performance`.
|
||||
- `resetInstrumentsForTesting()` clears the lazy cache so a re-imported
|
||||
test environment resolves the new provider.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { instrument } from "@suaveplan/better-auth-surrealdb/otel";
|
||||
|
||||
const row = await instrument({
|
||||
model: "session",
|
||||
operation: "create",
|
||||
run: async () => insertSessionRow(),
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import { instrument } from "@suaveplan/better-auth-surrealdb/otel";
|
||||
import { metrics } from "@opentelemetry/api";
|
||||
import { MeterProvider, PeriodicExportingMetricReader, InMemoryMetricExporter, AggregationTemporality } from "@opentelemetry/sdk-metrics";
|
||||
|
||||
// Install provider BEFORE first instrument() call:
|
||||
const exporter = new InMemoryMetricExporter(AggregationTemporality.CUMULATIVE);
|
||||
const reader = new PeriodicExportingMetricReader({ exporter, exportIntervalMillis: 60_000 });
|
||||
const provider = new MeterProvider({ readers: [reader] });
|
||||
metrics.setGlobalMeterProvider(provider);
|
||||
|
||||
await instrument({ model: "user", operation: "findOne", run: async () => ({ ok: true }) });
|
||||
await provider.forceFlush();
|
||||
const collected = exporter.getMetrics();
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `instrument<T>(op): Promise<T>` — wraps a `() => Promise<T>` with counter +
|
||||
histogram observations; rethrows on failure.
|
||||
- `InstrumentedOp<T>` — `{ model: string; operation: string; run: () => Promise<T> }`.
|
||||
- `AdapterOutcome` — `"success" | "error"`.
|
||||
- `resetInstrumentsForTesting()` — invalidates the lazy cache.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The lazy-init pattern is critical: importing OTel `metrics` at module load
|
||||
returns a noop meter unless `setGlobalMeterProvider` has been called by the
|
||||
host. Caching the meter+instruments after first use keeps subsequent calls
|
||||
allocation-free. `performance.now()` is preferred for sub-millisecond
|
||||
resolution but `Date.now()` is a correct fallback because the histogram
|
||||
records millisecond observations regardless.
|
||||
Reference in New Issue
Block a user