Prometheus.res
1 // SPDX-License-Identifier: MIT 2 // SPDX-FileCopyrightText: 2025 Jonathan D.A. Jewell 3 4 /// Prometheus metrics adapter 5 /// Connects to Prometheus HTTP API for metrics queries 6 7 open Adapter 8 9 let baseUrl = ref(Deno.Env.getWithDefault("PROMETHEUS_URL", "http://localhost:9090")) 10 let connected = ref(false) 11 12 let name = "prometheus" 13 let description = "Prometheus metrics server adapter" 14 15 let connect = async () => { 16 // Test connection to Prometheus 17 let url = baseUrl.contents ++ "/api/v1/status/buildinfo" 18 switch await Deno.Fetch.get(url) { 19 | Ok(_) => connected := true 20 | Error(e) => Exn.raiseError(`Failed to connect to Prometheus: ${e}`) 21 } 22 } 23 24 let disconnect = async () => { 25 connected := false 26 } 27 28 let isConnected = async () => connected.contents 29 30 // Query instant metrics 31 let queryHandler = async (args: dict<JSON.t>): JSON.t => { 32 let query = switch Dict.get(args, "query") { 33 | Some(JSON.String(q)) => q 34 | _ => Exn.raiseError("query parameter is required") 35 } 36 let time = switch Dict.get(args, "time") { 37 | Some(JSON.String(t)) => Some(t) 38 | _ => None 39 } 40 41 let url = switch time { 42 | Some(t) => `${baseUrl.contents}/api/v1/query?query=${query}&time=${t}` 43 | None => `${baseUrl.contents}/api/v1/query?query=${query}` 44 } 45 46 switch await Deno.Fetch.get(url) { 47 | Ok(data) => data 48 | Error(e) => Exn.raiseError(e) 49 } 50 } 51 52 // Query range metrics 53 let queryRangeHandler = async (args: dict<JSON.t>): JSON.t => { 54 let query = switch Dict.get(args, "query") { 55 | Some(JSON.String(q)) => q 56 | _ => Exn.raiseError("query parameter is required") 57 } 58 let start = switch Dict.get(args, "start") { 59 | Some(JSON.String(s)) => s 60 | _ => Exn.raiseError("start parameter is required") 61 } 62 let end_ = switch Dict.get(args, "end") { 63 | Some(JSON.String(e)) => e 64 | _ => Exn.raiseError("end parameter is required") 65 } 66 let step = switch Dict.get(args, "step") { 67 | Some(JSON.String(s)) => s 68 | _ => "15s" 69 } 70 71 let url = `${baseUrl.contents}/api/v1/query_range?query=${query}&start=${start}&end=${end_}&step=${step}` 72 73 switch await Deno.Fetch.get(url) { 74 | Ok(data) => data 75 | Error(e) => Exn.raiseError(e) 76 } 77 } 78 79 // List all series matching a selector 80 let seriesHandler = async (args: dict<JSON.t>): JSON.t => { 81 let match_ = switch Dict.get(args, "match") { 82 | Some(JSON.String(m)) => m 83 | _ => Exn.raiseError("match parameter is required") 84 } 85 86 let url = `${baseUrl.contents}/api/v1/series?match[]=${match_}` 87 88 switch await Deno.Fetch.get(url) { 89 | Ok(data) => data 90 | Error(e) => Exn.raiseError(e) 91 } 92 } 93 94 // List all label names 95 let labelsHandler = async (_args: dict<JSON.t>): JSON.t => { 96 let url = `${baseUrl.contents}/api/v1/labels` 97 98 switch await Deno.Fetch.get(url) { 99 | Ok(data) => data 100 | Error(e) => Exn.raiseError(e) 101 } 102 } 103 104 // List label values for a specific label 105 let labelValuesHandler = async (args: dict<JSON.t>): JSON.t => { 106 let label = switch Dict.get(args, "label") { 107 | Some(JSON.String(l)) => l 108 | _ => Exn.raiseError("label parameter is required") 109 } 110 111 let url = `${baseUrl.contents}/api/v1/label/${label}/values` 112 113 switch await Deno.Fetch.get(url) { 114 | Ok(data) => data 115 | Error(e) => Exn.raiseError(e) 116 } 117 } 118 119 // Get current targets 120 let targetsHandler = async (_args: dict<JSON.t>): JSON.t => { 121 let url = `${baseUrl.contents}/api/v1/targets` 122 123 switch await Deno.Fetch.get(url) { 124 | Ok(data) => data 125 | Error(e) => Exn.raiseError(e) 126 } 127 } 128 129 // Get current alerts 130 let alertsHandler = async (_args: dict<JSON.t>): JSON.t => { 131 let url = `${baseUrl.contents}/api/v1/alerts` 132 133 switch await Deno.Fetch.get(url) { 134 | Ok(data) => data 135 | Error(e) => Exn.raiseError(e) 136 } 137 } 138 139 // Get alerting rules 140 let rulesHandler = async (_args: dict<JSON.t>): JSON.t => { 141 let url = `${baseUrl.contents}/api/v1/rules` 142 143 switch await Deno.Fetch.get(url) { 144 | Ok(data) => data 145 | Error(e) => Exn.raiseError(e) 146 } 147 } 148 149 let tools: dict<toolDef> = Dict.fromArray([ 150 ("prometheus_query", { 151 description: "Execute an instant PromQL query", 152 params: Dict.fromArray([ 153 ("query", stringParam(~description="PromQL query expression")), 154 ("time", stringParam(~description="Evaluation timestamp (RFC3339 or Unix timestamp)")), 155 ]), 156 handler: queryHandler, 157 }), 158 ("prometheus_query_range", { 159 description: "Execute a range PromQL query", 160 params: Dict.fromArray([ 161 ("query", stringParam(~description="PromQL query expression")), 162 ("start", stringParam(~description="Start timestamp (RFC3339 or Unix)")), 163 ("end", stringParam(~description="End timestamp (RFC3339 or Unix)")), 164 ("step", stringParam(~description="Query step (e.g., 15s, 1m)")), 165 ]), 166 handler: queryRangeHandler, 167 }), 168 ("prometheus_series", { 169 description: "List time series matching a selector", 170 params: Dict.fromArray([ 171 ("match", stringParam(~description="Series selector (e.g., up{job=\"prometheus\"})")), 172 ]), 173 handler: seriesHandler, 174 }), 175 ("prometheus_labels", { 176 description: "List all label names", 177 params: Dict.make(), 178 handler: labelsHandler, 179 }), 180 ("prometheus_label_values", { 181 description: "List values for a specific label", 182 params: Dict.fromArray([ 183 ("label", stringParam(~description="Label name")), 184 ]), 185 handler: labelValuesHandler, 186 }), 187 ("prometheus_targets", { 188 description: "Get current scrape targets and their status", 189 params: Dict.make(), 190 handler: targetsHandler, 191 }), 192 ("prometheus_alerts", { 193 description: "Get current active alerts", 194 params: Dict.make(), 195 handler: alertsHandler, 196 }), 197 ("prometheus_rules", { 198 description: "Get alerting and recording rules", 199 params: Dict.make(), 200 handler: rulesHandler, 201 }), 202 ])