Files
suaveplan/packages/api/api-plugin-banking/src/otel/instruments.ts
2026-05-27 23:29:53 +01:00

83 lines
3.2 KiB
TypeScript

import { type Counter, type Histogram, metrics } from "@opentelemetry/api";
/**
* Bundle of OpenTelemetry instruments emitted by the banking plugin.
*
* - `banksTotal{op}` — bank lifecycle transitions. `op ∈
* "create" | "soft-delete" | "update"`.
* - `accountsTotal{op}` — account lifecycle transitions. `op ∈
* "create" | "delete" | "update" | "status-change"`.
* - `transfersTotal{outcome}` — `transferFunds` call counter.
* `outcome ∈ "success" | "insufficient_funds" | "error"`.
* - `transferAmount` — histogram of transfer amounts (units: currency
* minor units, treated as a unitless float for genericity).
* - `fundOperationsTotal{op,outcome}` — `addFunds` / `subtractFunds`.
* `op ∈ "add" | "subtract"`, `outcome ∈ "success" | "error" |
* "insufficient_funds"`.
* - `queryDurationMs{op}` — histogram of repository query wall-clock
* duration in ms, partitioned by op (`"bank.list"`, `"account.list"`,
* `"transfer"`, etc.).
*/
export interface BankingInstruments {
readonly banksTotal: Counter;
readonly accountsTotal: Counter;
readonly transfersTotal: Counter;
readonly transferAmount: Histogram;
readonly fundOperationsTotal: Counter;
readonly queryDurationMs: Histogram;
}
let cached: BankingInstruments | undefined;
/**
* Lazily build (and cache) the banking instruments bundle against the
* current global `MeterProvider`. Call {@link resetBankingInstruments}
* in test setup to force a re-bind against a freshly-installed
* provider.
*/
export function getBankingInstruments(): BankingInstruments {
if (cached !== undefined) return cached;
const meter = metrics.getMeter("@suaveplan/api-plugin-banking");
cached = {
banksTotal: meter.createCounter("banking.banks_total", {
description:
"Total bank lifecycle transitions, partitioned by op label",
}),
accountsTotal: meter.createCounter("banking.accounts_total", {
description:
"Total bank-account lifecycle transitions, partitioned by op label",
}),
transfersTotal: meter.createCounter("banking.transfers_total", {
description:
"Total transferFunds() calls, partitioned by outcome label",
}),
transferAmount: meter.createHistogram("banking.transfer_amount", {
description:
"Histogram of transfer amounts (unitless float; treat as minor currency units)",
}),
fundOperationsTotal: meter.createCounter(
"banking.fund_operations_total",
{
description:
"Total addFunds/subtractFunds calls, partitioned by op + outcome",
}
),
queryDurationMs: meter.createHistogram("banking.query_duration_ms", {
description:
"Wall-clock duration of banking repository queries in ms",
unit: "ms",
}),
};
return cached;
}
/**
* Drop the cached instrument bundle so the next
* {@link getBankingInstruments} call rebinds against the global
* `MeterProvider` in effect at that point. Tests call this after
* installing a fresh `MeterProvider`.
*/
export function resetBankingInstruments(): void {
cached = undefined;
}