/** * Metric definitions for Weft observability. * * These constants describe the metrics emitted by Weft interceptors. They * follow OpenTelemetry semantic conventions where applicable, and can be * consumed by any metrics backend that accepts name/description/unit tuples. * * @module metrics */ import type { MetricsSnapshot } from './metrics-snapshot.ts'; import type { OpenTelemetryMeter } from './no-op-telemetry'; export { METRICS } from './metrics-catalog.ts'; export type { CounterMetric, GaugeMetric, HistogramMetric, MetricDefinition, MetricType, MetricsSnapshot, } from './metrics-snapshot.ts'; /** * Collects counters, histograms, and gauges for Weft observability. * * Single-threaded — each worker constructs its own collector; concurrent * `record()`/`increment()` calls from the same isolate do not require locking. * Call {@link snapshot} to read all collected values and {@link reset} to * clear them. * * @example * ```ts * import { MetricsCollector } from '@lostgradient/weft/observability'; * * const collector = new MetricsCollector(); * collector.increment('weft.workflow.started'); * collector.record('weft.workflow.duration', 42); * console.log(collector.snapshot()); * ``` */ export declare class MetricsCollector { #private; constructor(); /** Increment a counter by `value` (default 1). */ increment(name: string, value?: number): void; /** Record a histogram observation. Values are kept in a circular buffer capped at {@link MAX_HISTOGRAM_SAMPLES}. */ record(name: string, value: number): void; /** Set an absolute gauge value. */ gauge(name: string, value: number): void; /** Return a point-in-time snapshot of all collected metrics. */ snapshot(): MetricsSnapshot; /** Clear all collected metrics. */ reset(): void; } /** * OpenTelemetry instrument set for Weft metrics. * * @example * ```ts * import { createOpenTelemetryMetrics, type OpenTelemetryMetrics } from '@lostgradient/weft/observability'; * * const openTelemetryMetrics: OpenTelemetryMetrics = createOpenTelemetryMetrics('my-service'); * openTelemetryMetrics.workflowDuration.record(120); * openTelemetryMetrics.activityAttempts.add(1); * ``` */ export type OpenTelemetryMetrics = { workflowDuration: { record(value: number, attributes?: Record): void; }; activityDuration: { record(value: number, attributes?: Record): void; }; activityAttempts: { add(value: number, attributes?: Record): void; }; activeWorkflows: { add(value: number, attributes?: Record): void; }; }; /** * Create OpenTelemetry instruments for the standard Weft metrics. * * Accepts an `OpenTelemetryMeter` instance, a string meter name, or nothing. When * called without arguments it uses `getOpenTelemetryApi().metrics.getMeter('weft')`, * which returns a no-op meter when `@opentelemetry/api` is not installed. * * @example * ```ts * import { createOpenTelemetryMetrics } from '@lostgradient/weft/observability'; * * // Uses the auto-detected OpenTelemetry API or no-op fallback * const instruments = createOpenTelemetryMetrics('my-service'); * instruments.workflowDuration.record(250, { workflow_type: 'greet' }); * instruments.activityAttempts.add(1, { activity: 'sendEmail' }); * ``` */ export declare function createOpenTelemetryMetrics(meterOrName?: OpenTelemetryMeter | string): OpenTelemetryMetrics; /** * Pluggable interface for producing Prometheus text-format output at * `/v1/metrics`. Weft ships with a default implementation that serializes a * {@link MetricsCollector} snapshot, but consumers who already use OpenTelemetry can * adapt `@opentelemetry/exporter-prometheus` (or any other source) to this * interface and pass it via `HandlerOptions.prometheusExporter`. * * Keeping this as an interface rather than hard-wiring the OpenTelemetry SDK avoids * pulling `@opentelemetry/sdk-metrics` into the runtime footprint while still * giving projects that *do* want full OpenTelemetry a clean plug point. * * > [!WARNING] `/v1/metrics` is unauthenticated by default * > The Weft server treats `/v1/metrics` as a public path (see * > `DEFAULT_PUBLIC_PATHS` in `src/server/authentication.ts`) so that * > Prometheus scrapers can read it without credentials. The default * > {@link createMetricsCollectorExporter} only emits aggregate counters and * > histograms with no labels, which is safe to expose. **A custom * > `PrometheusExporter` that emits labels — especially labels containing * > user identifiers, request paths with IDs, or any * > other PII — will leak that data to anyone who can reach the endpoint.** * > * > If your exporter emits sensitive labels, override the default by setting * > `auth.publicPaths` on the server options to a list that does *not* * > include `/v1/metrics`, then scrape it with an authenticated client. */ export interface PrometheusExporter { /** * Produce Prometheus text-format output for the current state of the metrics * source. Must be safe to call repeatedly — each invocation should reflect * the latest values. */ serialize(): string | Promise; } /** * Serialize a {@link MetricsSnapshot} as Prometheus text format using the * definitions registered in {@link METRICS}. Metrics that aren't in the * snapshot still emit their `# HELP` / `# TYPE` lines with zero values so * Prometheus scrapers see a stable schema. * * @example * ```ts * import { MetricsCollector, serializeMetricsSnapshotForPrometheus } from '@lostgradient/weft/observability'; * * const collector = new MetricsCollector(); * collector.increment('weft.workflow.started'); * const body = serializeMetricsSnapshotForPrometheus(collector.snapshot()); * console.log(body.includes('weft_workflow_started_total')); * ``` */ export declare function serializeMetricsSnapshotForPrometheus(snapshot: MetricsSnapshot): string; /** * Default {@link PrometheusExporter} that sources its values from a * {@link MetricsCollector}. Equivalent to the previous inline serializer in * the server's `/v1/metrics` handler — extracted here so it can be reused and * so a custom implementation can be substituted without touching the server. * * @example * ```ts * import { createMetricsCollectorExporter } from '@lostgradient/weft/observability'; * * const exporter = createMetricsCollectorExporter(undefined); * // Pass to serve() to expose /v1/metrics * // serve({ engine, prometheusExporter: exporter }); * console.log(typeof exporter.serialize); // 'function' * ``` */ export declare function createMetricsCollectorExporter(collector: MetricsCollector | undefined): PrometheusExporter;