UNPKG

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

156 lines (152 loc) 5.23 kB
/** * Recipe: scrape-endpoint * * Scrapes Prometheus metrics from any HTTP endpoint. * The most common recipe — covers any app that exposes /metrics. * * Generated Alloy pipeline: * prometheus.scrape → prometheus.remote_write */ import { credentialEnvVar } from "../types.js"; import { AlloyConfigBuilder, componentLabel, escapeString, renderTargets, } from "../../config-builder.js"; const recipe = { name: "scrape-endpoint", category: "metrics", signal: "metrics", summary: "Scrape Prometheus metrics from any HTTP endpoint", dashboardTemplate: "metric-explorer", credentialParams: ["basicAuth", "bearerToken"], requiredParams: [ { name: "url", type: "string", description: "Full URL to scrape (host:port or http://host:port/path)", example: "http://myapp:8080/metrics", }, ], optionalParams: [ { name: "scrapeInterval", type: "string", description: "How often to scrape", default: "15s", }, { name: "metricsPath", type: "string", description: "Path to the metrics endpoint", default: "/metrics", }, { name: "jobName", type: "string", description: "Prometheus job label value", }, { name: "basicAuth", type: "string", description: "Basic auth credentials (user:password)", sensitive: true, example: "admin:secret", }, { name: "bearerToken", type: "string", description: "Bearer token for authentication", sensitive: true, }, { name: "tlsInsecure", type: "boolean", description: "Skip TLS certificate verification", default: false, }, ], generateConfig(pipelineId, params, targets, pipelineName) { const url = params.url; const scrapeInterval = params.scrapeInterval || "15s"; const metricsPath = params.metricsPath || "/metrics"; const jobName = params.jobName || deriveJobName(url); const tlsInsecure = params.tlsInsecure; const basicAuth = params.basicAuth; const bearerToken = params.bearerToken; // Parse URL to extract address const { address, scheme } = parseTarget(url); const scrapeLabel = componentLabel(pipelineId, "scrape"); const writeLabel = componentLabel(pipelineId, "write"); const builder = new AlloyConfigBuilder(); // Build scrape block let scrapeBlock = `prometheus.scrape "${scrapeLabel}" { targets = ${renderTargets([{ address }])} forward_to = [prometheus.remote_write.${writeLabel}.receiver] job_name = "${escapeString(jobName)}" scrape_interval = "${escapeString(scrapeInterval)}" metrics_path = "${escapeString(metricsPath)}" scheme = "${scheme}"`; if (basicAuth) { scrapeBlock += ` basic_auth { username = sys.env("${credentialEnvVar("scrape-endpoint", pipelineName, "basicAuth_user")}") password = sys.env("${credentialEnvVar("scrape-endpoint", pipelineName, "basicAuth_pass")}") }`; } if (bearerToken) { scrapeBlock += ` authorization { type = "Bearer" credentials = sys.env("${credentialEnvVar("scrape-endpoint", pipelineName, "bearerToken")}") }`; } if (tlsInsecure) { scrapeBlock += ` tls_config { insecure_skip_verify = true }`; } scrapeBlock += "\n}"; builder.addBlock(scrapeBlock); // Build remote_write block builder.addBlock(`prometheus.remote_write "${writeLabel}" { endpoint { url = "${escapeString(targets.prometheusRemoteWriteUrl)}" } }`); return builder.build(pipelineId, "scrape-endpoint", jobName); }, sampleQueries(_params, jobName) { return { upCheck: `up{job="${jobName}"}`, scrapeRate: `scrape_samples_scraped{job="${jobName}"}`, }; }, componentIds(pipelineId) { return [ `prometheus.scrape.${componentLabel(pipelineId, "scrape")}`, `prometheus.remote_write.${componentLabel(pipelineId, "write")}`, ]; }, }; // ── Helpers ────────────────────────────────────────────────────────── function parseTarget(url) { try { const parsed = new URL(url); return { address: `${parsed.hostname}${parsed.port ? ":" + parsed.port : ""}`, scheme: parsed.protocol.replace(":", "") || "http", }; } catch { // If URL parsing fails, treat as host:port return { address: url.replace(/^https?:\/\//, ""), scheme: "http" }; } } function deriveJobName(url) { try { const parsed = new URL(url); return parsed.hostname.replace(/\./g, "-"); } catch { return url.replace(/[^a-zA-Z0-9_-]/g, "-").slice(0, 30); } } export default recipe;