Initial Commit
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
# `observability/metrics` — OTel histograms and counters
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
The `observability/metrics` module owns the OpenTelemetry surface of
|
||||
`@suaveplan/tpm-struct`. Every public parser invocation records into
|
||||
one of three histograms and one of three counters; the histograms
|
||||
bucket wall-time by `outcome` (and by `type` / `name_alg` where it adds
|
||||
value), and the counters expose hit / error rates with low-cardinality
|
||||
attributes drawn from the bounded `TPM_ALG`, `TPM_STRUCT_CODES`, and
|
||||
`PublicBranch` enums. The full surface is what makes a running TPM-
|
||||
attestation gateway observable: a sudden spike in
|
||||
`tpm_struct.parse_errors_total{code="TPM_STRUCT.MAGIC_INVALID"}` is
|
||||
the canonical signal that an upstream is sending corrupted or
|
||||
non-TPM blobs.
|
||||
|
||||
## Features
|
||||
|
||||
- Three histograms: `parse_tpms_attest_duration_ms{outcome}`,
|
||||
`parse_tpmt_public_duration_ms{type,outcome}`,
|
||||
`compute_name_duration_ms{name_alg}`.
|
||||
- Three counters: `parse_total{kind,outcome}`, `parse_errors_total{code}`,
|
||||
`alg_total{alg}`.
|
||||
- Every attribute value is a bounded enum (no free-form strings).
|
||||
- Lazy instrument resolution — instruments are created on first call so
|
||||
consumers can install a custom `MeterProvider` before any module-load
|
||||
side effects.
|
||||
- `resetTpmStructMetricsForTesting()` clears the instrument cache for
|
||||
tests that swap the global provider between cases.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { metrics } from "@opentelemetry/api";
|
||||
import { parseTpmsAttest } from "@suaveplan/tpm-struct";
|
||||
|
||||
// Once at app boot, install your MeterProvider.
|
||||
metrics.setGlobalMeterProvider(myProvider);
|
||||
|
||||
// Subsequent calls emit metrics automatically.
|
||||
parseTpmsAttest(attestBytes);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import {
|
||||
metrics,
|
||||
type Counter,
|
||||
type Histogram,
|
||||
type Meter,
|
||||
type MeterProvider,
|
||||
} from "@opentelemetry/api";
|
||||
import {
|
||||
parseTpmsAttest,
|
||||
resetTpmStructMetricsForTesting,
|
||||
} from "@suaveplan/tpm-struct";
|
||||
|
||||
// In-memory provider for assertions in tests.
|
||||
function recordingProvider() {
|
||||
const counters: { name: string; value: number; attrs?: Record<string, string> }[] = [];
|
||||
const histograms: typeof counters = [];
|
||||
const meter: Partial<Meter> = {
|
||||
createCounter: (name: string) =>
|
||||
({
|
||||
add: (v: number, a?: Record<string, string>) =>
|
||||
counters.push({ name, value: v, attrs: a }),
|
||||
}) as unknown as Counter,
|
||||
createHistogram: (name: string) =>
|
||||
({
|
||||
record: (v: number, a?: Record<string, string>) =>
|
||||
histograms.push({ name, value: v, attrs: a }),
|
||||
}) as unknown as Histogram,
|
||||
};
|
||||
const provider: Partial<MeterProvider> = {
|
||||
getMeter: () => meter as Meter,
|
||||
};
|
||||
return { provider: provider as MeterProvider, counters, histograms };
|
||||
}
|
||||
|
||||
const { provider, counters, histograms } = recordingProvider();
|
||||
metrics.setGlobalMeterProvider(provider);
|
||||
resetTpmStructMetricsForTesting();
|
||||
parseTpmsAttest(certifyBytes);
|
||||
console.log(counters, histograms);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- **`recordAttestDuration(outcome, durationMs)`** — record a
|
||||
`parse_tpms_attest_duration_ms` observation.
|
||||
- **`recordPublicDuration(type, outcome, durationMs)`** — record a
|
||||
`parse_tpmt_public_duration_ms` observation.
|
||||
- **`recordNameDuration(nameAlg, durationMs)`** — record a
|
||||
`compute_name_duration_ms` observation.
|
||||
- **`recordParseDuration(kind, branchOrAlg, outcome, durationMs)`** —
|
||||
dispatch helper used by every parser.
|
||||
- **`recordParseTotal(kind, outcome)`** — increment `parse_total`.
|
||||
- **`recordParseError(err)`** — increment `parse_errors_total{code}`
|
||||
using the registered code when `err` is a `TpmStructError`, or
|
||||
`"TPM_STRUCT.OTHER"` otherwise (sentinel label, not a registry
|
||||
member).
|
||||
- **`recordAlgObserved(alg)`** — increment `alg_total{alg}`.
|
||||
- **`resetTpmStructMetricsForTesting()`** — clear the cached instruments.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Instruments are cached at module scope so the per-call cost stays a
|
||||
single `Map.get`. The `OTHER` sentinel on `parse_errors_total` is a
|
||||
defensive label — the parsers only ever throw `TpmStructError`, but
|
||||
the helper accepts `unknown` because library consumers may forward
|
||||
arbitrary failures through a wrapping function.
|
||||
Reference in New Issue
Block a user