openclaw-grafana-lens
Version:
OpenClaw plugin that gives AI agents full Grafana access — 18 composable tools for PromQL/LogQL/TraceQL queries, dashboard creation, alerting, SRE investigation, security monitoring, data collection pipeline management via Grafana Alloy (29 recipes), and
120 lines (119 loc) • 4.21 kB
TypeScript
/**
* Custom Metrics Store
*
* Manages dynamic OTel ObservableGauge/Counter instances registered at runtime
* by the agent via the grafana_push_metrics tool. Provides:
*
* - Validation: name regex, label consistency, cardinality limits
* - Persistence: gauge values survive restarts (JSON file in stateDir)
* - TTL eviction: optional auto-expiry for stale metrics
*
* All metric names are enforced to start with `openclaw_ext_` to prevent
* collision with built-in `openclaw_lens_*` metrics.
*
* Data flow:
* Agent pushes data → store updates value maps → OTel ObservableGauge callbacks
* read values at export time → OTLP push → Collector → Mimir → Grafana
*/
import type { Meter, Counter as OtelCounter } from "@opentelemetry/api";
import type { OtlpJsonWriter } from "./otlp-json-writer.js";
export type MetricType = "gauge" | "counter";
export type CustomMetricDefinition = {
name: string;
type: MetricType;
help: string;
labelNames: string[];
createdAt: number;
updatedAt: number;
ttlMs?: number;
};
export type CustomMetricDataPoint = {
name: string;
value: number;
labels?: Record<string, string>;
type?: MetricType;
help?: string;
ttlDays?: number;
/** ISO 8601 timestamp for historical data (e.g., "2025-01-15"). Omit for real-time. */
timestamp?: string;
};
export type CustomMetricsLimits = {
maxMetrics?: number;
maxLabelsPerMetric?: number;
maxLabelValues?: number;
};
export type PushResult = {
accepted: number;
rejected: Array<{
name: string;
reason: string;
}>;
/** Mapping of normalized metric name → exact PromQL name (counters get _total suffix). */
queryNames: Record<string, string>;
};
export declare function normalizeMetricName(name: string): {
normalized: string;
wasAutoPrepended: boolean;
};
/** Get the exact PromQL name for a metric — counters get `_total` suffix per Prometheus convention. */
export declare function getPromQLName(name: string, type: MetricType): string;
type Logger = {
info(msg: string): void;
warn(msg: string): void;
error(msg: string): void;
};
export declare class CustomMetricsStore {
private meter;
private forceFlushFn;
private stateDir;
private logger;
private limits;
private definitions;
/** Gauge value store: metricName → serializedLabels → value */
private gaugeValues;
/** OTel Counter instances for counter-type metrics */
private counters;
private labelValueCounts;
private flushInterval;
/** Set of metric names that have had ObservableGauge registered */
private registeredGaugeNames;
private otlpWriter;
private pushCounter;
constructor(meter: Meter, forceFlush: () => Promise<void>, stateDir: string, logger: Logger, limits?: CustomMetricsLimits, otlpWriter?: OtlpJsonWriter | null, pushCounter?: OtelCounter | null);
load(): Promise<void>;
flush(): Promise<void>;
startPeriodicFlush(): void;
stopPeriodicFlush(): Promise<void>;
registerMetric(def: {
name: string;
type: MetricType;
help: string;
labelNames: string[];
ttlMs?: number;
createdAt?: number;
updatedAt?: number;
}): CustomMetricDefinition;
pushValues(points: CustomMetricDataPoint[]): PushResult;
/**
* Force an immediate OTLP export so pushed data is available right away.
*/
forceFlush(): Promise<void>;
/**
* Record push statistics for the `openclaw_lens_custom_metrics_pushed_total` counter.
*/
trackPush(accepted: number, rejected: number): void;
/**
* Validate and normalize a data point: value check, name normalization,
* auto-registration, label validation, cardinality check.
* Returns { normalized, labels, def } on success, throws on failure.
*/
private validateAndNormalize;
private pushSingleValue;
pushTimestampedValues(points: CustomMetricDataPoint[]): Promise<PushResult>;
listMetrics(): CustomMetricDefinition[];
deleteMetric(name: string): boolean;
private trackLabelValues;
private checkCardinality;
private evictExpired;
}
export {};