Files
skills/docs/foundation/timing/cron/src/otel/cron-otel.md
2026-05-29 08:20:56 +01:00

2.8 KiB

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

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

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.