Files
pikasTech-unidesk/scripts/src/hwlab-node-observability-promql.ts
T
2026-06-18 04:28:17 +00:00

66 lines
3.7 KiB
TypeScript

// SPEC: PJ2026-01060505 Workbench Performance draft-2026-06-17-p0.
// Responsibility: Prometheus/observability expression rendering for HWLAB node runtime.
import type { HwlabRuntimeObservabilityRecordingRuleSpec, HwlabRuntimeObservabilitySpec, HwlabRuntimeObservabilityWarningAlertSpec } from "./hwlab-node-lanes";
export function nodeObservabilityRecordingRuleSummaries(observability: HwlabRuntimeObservabilitySpec): Array<Record<string, unknown>> {
return observability.recordingRules.map((rule) => ({
...rule,
expression: nodeObservabilityRecordingRuleExpression(rule),
sampleCountExpression: nodeObservabilitySampleCountExpression(rule, rule.matchLabels),
lowSampleGuardExpression: `${nodeObservabilitySampleCountExpression(rule, rule.matchLabels)} >= ${rule.minSamples}`,
}));
}
export function nodeObservabilityWarningAlertSummaries(observability: HwlabRuntimeObservabilitySpec): Array<Record<string, unknown>> {
const recordingRules = new Map(observability.recordingRules.map((rule) => [rule.id, rule]));
return observability.warningAlerts.map((alert) => {
const rule = recordingRules.get(alert.ruleId);
return {
...alert,
expression: rule === undefined ? null : nodeObservabilityWarningAlertExpression(rule, alert),
recordingRule: rule === undefined ? null : rule.metric,
lowSampleGuardExpression: rule === undefined ? null : `${nodeObservabilitySampleCountExpression(rule, { ...rule.matchLabels, ...alert.matchLabels })} >= ${alert.minSamples}`,
};
});
}
export function nodeObservabilityRecordingRuleExpression(rule: HwlabRuntimeObservabilityRecordingRuleSpec): string {
const bucketGroupBy = uniqueStrings([...rule.groupBy, "le"]);
return `histogram_quantile(${formatPrometheusNumber(rule.quantile)}, sum by (${bucketGroupBy.join(", ")}) (rate(${prometheusMetricSelector(`${rule.sourceMetric}_bucket`, rule.matchLabels)}[${rule.window}])))`;
}
function nodeObservabilitySampleCountExpression(rule: HwlabRuntimeObservabilityRecordingRuleSpec, matchLabels: Record<string, string>): string {
const groupBy = uniqueStrings([...rule.groupBy]);
return `sum by (${groupBy.join(", ")}) (increase(${prometheusMetricSelector(`${rule.sourceMetric}_count`, matchLabels)}[${rule.window}]))`;
}
export function nodeObservabilityWarningAlertExpression(rule: HwlabRuntimeObservabilityRecordingRuleSpec, alert: HwlabRuntimeObservabilityWarningAlertSpec): string {
const sampleLabels = { ...rule.matchLabels, ...alert.matchLabels };
const sampleGuard = `${nodeObservabilitySampleCountExpression(rule, sampleLabels)} >= ${alert.minSamples}`;
const vectorMatch = rule.groupBy.length === 0 ? "" : ` on (${uniqueStrings([...rule.groupBy]).join(", ")})`;
return `(${prometheusMetricSelector(rule.metric, alert.matchLabels)} > ${formatPrometheusNumber(alert.thresholdSeconds)}) and${vectorMatch} (${sampleGuard})`;
}
function prometheusMetricSelector(metric: string, labels: Record<string, string>): string {
const selector = prometheusLabelSelector(labels);
return selector.length === 0 ? metric : `${metric}${selector}`;
}
function prometheusLabelSelector(labels: Record<string, string>): string {
const entries = Object.entries(labels);
if (entries.length === 0) return "";
return `{${entries.map(([key, value]) => `${key}${value.includes("|") ? "=~" : "="}"${escapePrometheusLabelValue(value)}"`).join(",")}}`;
}
function escapePrometheusLabelValue(value: string): string {
return value.replace(/\\/gu, "\\\\").replace(/"/gu, "\\\"");
}
function formatPrometheusNumber(value: number): string {
return Number.isInteger(value) ? String(value) : String(value);
}
function uniqueStrings(values: readonly string[]): string[] {
return [...new Set(values)];
}