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,65 @@
# otel
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
OpenTelemetry instrumentation for `@suaveplan/db-sqlite`. The `withQuerySpan` function wraps any async database operation in a named OTel span, setting `db.system=sqlite` and `db.statement` (sanitised) as span attributes, recording exceptions on failure, and emitting latency and count metrics. It is a no-op when no OTel SDK is registered — the `@opentelemetry/api` no-op provider handles all calls silently.
`sanitizeSql` strips string literals and numeric values from SQL strings before attaching them to span attributes, preventing PII leakage into telemetry backends. Parameterised queries still record their structure (`SELECT * FROM users WHERE id = ?`) without revealing actual parameter values.
## Features
- `withQuerySpan(operationName, rawSql, fn)` — wraps fn in a named span with `db.system=sqlite` and sanitised `db.statement`
- `sanitizeSql(sql)` — replaces string literals, quoted identifiers, and bare numbers with `?` placeholders
- Histograms: `sqlite.query.duration_ms` (labels: `operation`)
- Counters: `sqlite.query.count`, `sqlite.error.count` (labels: `operation`)
- `resetOtelInstruments()` — clears the lazy instruments singleton for test isolation
## Usage: Basic Example
```ts
import { withQuerySpan } from "@suaveplan/db-sqlite/otel";
const rows = await withQuerySpan(
"db.query",
"SELECT * FROM users WHERE id = 42",
async () => sqlite.query("SELECT * FROM users WHERE id = ?").all([42])
);
```
## Usage: Advanced Example
```ts
import { sanitizeSql, withQuerySpan } from "@suaveplan/db-sqlite/otel";
// Preview sanitisation before instrumentation
const safe = sanitizeSql("SELECT * FROM t WHERE name = 'Alice' AND age = 30");
// => "SELECT * FROM t WHERE name = ? AND age = ?"
const result = await withQuerySpan("db.execute", safe, async () => {
sqlite.run("INSERT INTO events VALUES (?, ?)", ["login", 1234567890]);
return { rowsAffected: 1 };
});
```
## API Reference
### `withQuerySpan<T>(operationName, rawSql, fn): Promise<T>`
Starts a span named `operationName`, sets `db.system=sqlite` and `db.statement=sanitizeSql(rawSql)`, then calls `fn()`. On success records latency and increments `sqlite.query.count`. On failure records exception, sets `ERROR` status, and increments `sqlite.error.count`. Always ends the span.
### `sanitizeSql(sql: string): string`
Replaces single-quoted strings, double-quoted strings, and numeric literals (integer and decimal) with `?`. Safe to call on arbitrary SQL strings — does not parse SQL, only applies a regex substitution.
### `resetOtelInstruments(): void`
Clears the lazy OTel instruments singleton. Use in tests so each suite starts with fresh counter baselines. Also called internally by `SqliteAdapter.disconnect()`.
## Implementation Notes
The instruments lazy singleton is initialised on the first `withQuerySpan` call rather than at module load to ensure the OTel provider is registered before the meter is obtained. Tests that call `resetOtelInstruments()` between suites prevent counter state from leaking across test files.
`sanitizeSql` uses a single-pass regex and is intentionally simple — it does not understand SQL syntax, so complex nested strings or edge-case identifiers may not be fully sanitised. This is acceptable for telemetry purposes where approximate sanitisation is sufficient.