# cron-otel _(no submodules)_ ## Purpose `cron-otel` is the OpenTelemetry instrumentation surface for the `@suaveplan/cron` package. It centralises every metric emitted by the parser, the next-run engine, and the in-process scheduler so cardinality stays bounded and label semantics stay consistent. Each helper is a thin, no-throw wrapper around the global `metrics.getMeter("@suaveplan/cron")` instruments and is safe to call on every hot-path invocation — the underlying instrument is lazily constructed and then cached. ## Features - Two histograms: `cron.parse.duration_ms` (outcome) and `cron.next_run.duration_ms` (outcome + tz). - Three counters: `cron.parse_total`, `cron.next_run_total`, and `cron.dst_transition_total`. - Closed-enum `outcome` and `kind` attributes to keep cardinality bounded; `tz` is the caller-supplied IANA name pass-through. - Lazy meter construction — no metrics work happens until first record. ## Usage: Basic Example ```typescript import { recordParse, recordNextRun } from "@suaveplan/cron"; const t0 = Date.now(); try { // …parse cron expression… recordParse(Date.now() - t0, "success"); } catch (err) { recordParse(Date.now() - t0, "error"); throw err; } recordNextRun(0.5, "success", "America/New_York"); ``` ## Usage: Advanced Example ```typescript import { recordDstTransition } from "@suaveplan/cron"; function instrumentNextRun(detected: "spring-forward" | "fall-back" | null, tz: string) { if (detected !== null) { recordDstTransition(detected, tz); } } instrumentNextRun("spring-forward", "America/New_York"); instrumentNextRun("fall-back", "Europe/London"); instrumentNextRun(null, "UTC"); // no-op ``` ## API Reference - `recordParse(durationMs: number, outcome: "success" | "error"): void`. - `recordNextRun(durationMs: number, outcome: "success" | "error", tz: string): void`. - `recordDstTransition(kind: "spring-forward" | "fall-back", tz: string): void`. - `type Outcome = "success" | "error"` — exported helper alias. ## Implementation Notes Each instrument is materialised through `metrics.getMeter(...)` only on the first record call. Subsequent invocations reuse the module-scoped cached handle, so the hot path is a single `record`/`add` call on the shared instrument. The OpenTelemetry global meter provider is read each time — that's a no-op when no provider is installed, which lets the package ship metrics-ready without forcing consumers to wire up an SDK. Attribute cardinality is held below practical limits: `outcome` and `kind` are closed unions, and `tz` is passed through unmodified from the caller (consumers control their own tz vocabulary). The package does not emit logs or spans — those concerns live in higher-tier consumers like `@suaveplan/job-scheduler`.