Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
# dmarc-otel
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides OpenTelemetry metric instruments for `@suaveplan/dmarc`. It gives operators visibility into parse throughput and latency without coupling the parse logic itself to observability concerns. All instruments are created lazily by the OTel SDK's no-op implementation, so importing this module in an environment without an OTel SDK installed is always safe — the calls become no-ops.
## Features
- `dmarcParseTotal` — counter tracking total parse invocations by `kind` and `status`
- `dmarcParseDurationMs` — histogram recording parse latency in milliseconds by `kind`
- `recordParse()` — convenience wrapper that updates both instruments in one call
- Zero runtime dependencies beyond `@opentelemetry/api`
## Usage: Basic Example
```ts
import { recordParse } from "@suaveplan/dmarc/otel/dmarc-otel";
const start = performance.now();
try {
// ... parse logic
recordParse("aggregate", "success", performance.now() - start);
} catch {
recordParse("aggregate", "error", performance.now() - start);
throw;
}
```
## Usage: Advanced Example
```ts
import { dmarcParseTotal, dmarcParseDurationMs, recordParse } from "@suaveplan/dmarc/otel/dmarc-otel";
// Direct instrument access for custom attribute sets
dmarcParseTotal.add(1, { kind: "aggregate", status: "success", source: "s3" });
dmarcParseDurationMs.record(42, { kind: "forensic", source: "email" });
// Or use the convenience wrapper for standard usage
recordParse("forensic", "success", 38);
```
## API Reference
### `dmarcParseTotal`
`Counter` — incremented once per parse call. Attributes: `kind` (`"aggregate" | "forensic"`), `status` (`"success" | "error"`).
### `dmarcParseDurationMs`
`Histogram` — records elapsed parse time in milliseconds. Attributes: `kind` (`"aggregate" | "forensic"`).
### `recordParse(kind, status, durationMs): void`
Convenience function that increments `dmarcParseTotal` and records `durationMs` in `dmarcParseDurationMs` in a single call.
**Parameters:**
- `kind` — `"aggregate"` or `"forensic"`
- `status` — `"success"` or `"error"`
- `durationMs` — elapsed time in milliseconds
**Returns:** `void`
## Implementation Notes
The meter name is `"@suaveplan/dmarc"` which matches the npm package name, making it trivial to filter metrics by package in dashboards. Histogram bucket boundaries are left to the OTel SDK/exporter configuration rather than hardcoded here, which is the recommended practice per the OTel specification. Importing this module in test environments without an SDK causes zero issues because the OTel API ships a built-in no-op implementation.

View File

@@ -0,0 +1,42 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import {
dmarcParseDurationMs,
dmarcParseTotal,
recordParse,
} from "./dmarc-otel.js";
describe("dmarc-otel", () => {
it("exports dmarcParseTotal counter", { timeout: 5000 }, () => {
expect(dmarcParseTotal).toBeDefined();
expect(typeof dmarcParseTotal.add).toBe("function");
});
it("exports dmarcParseDurationMs histogram", { timeout: 5000 }, () => {
expect(dmarcParseDurationMs).toBeDefined();
expect(typeof dmarcParseDurationMs.record).toBe("function");
});
it("recordParse does not throw for aggregate success", {
timeout: 5000,
}, () => {
expect(() => recordParse("aggregate", "success", 10)).not.toThrow();
});
it("recordParse does not throw for aggregate error", {
timeout: 5000,
}, () => {
expect(() => recordParse("aggregate", "error", 5)).not.toThrow();
});
it("recordParse does not throw for forensic success", {
timeout: 5000,
}, () => {
expect(() => recordParse("forensic", "success", 20)).not.toThrow();
});
it("recordParse does not throw for forensic error", {
timeout: 5000,
}, () => {
expect(() => recordParse("forensic", "error", 3)).not.toThrow();
});
});

View File

@@ -0,0 +1,41 @@
import { metrics } from "@opentelemetry/api";
const meter = metrics.getMeter("@suaveplan/dmarc");
/**
* Counter tracking the total number of DMARC parse operations, labelled by
* `kind` (`aggregate` or `forensic`) and `status` (`success` or `error`).
*/
export const dmarcParseTotal = meter.createCounter("dmarc.parse.total", {
description: "Total number of DMARC report parse operations",
unit: "{reports}",
});
/**
* Histogram recording the wall-clock duration of each parse operation in
* milliseconds, labelled by `kind` (`aggregate` or `forensic`).
*/
export const dmarcParseDurationMs = meter.createHistogram(
"dmarc.parse.duration_ms",
{
description:
"Duration of DMARC report parse operations in milliseconds",
unit: "ms",
}
);
/**
* Records a completed parse observation.
*
* @param kind - `"aggregate"` or `"forensic"`.
* @param status - `"success"` or `"error"`.
* @param durationMs - Elapsed milliseconds for the parse call.
*/
export function recordParse(
kind: "aggregate" | "forensic",
status: "success" | "error",
durationMs: number
): void {
dmarcParseTotal.add(1, { kind, status });
dmarcParseDurationMs.record(durationMs, { kind });
}

View File

@@ -0,0 +1,5 @@
export {
dmarcParseDurationMs,
dmarcParseTotal,
recordParse,
} from "./dmarc-otel";