{"version":3,"sources":["vendor/metrics-providers/prometheus.js"],"sourcesContent":["/**\n * Copyright IBM Corp. 2024, 2026\n * SPDX-License-Identifier: BUSL-1.1\n */\n\n/*eslint no-console: \"off\"*/\n(function () {\n  var emptySeries = { unitSuffix: '', labels: {}, data: [] };\n\n  var prometheusProvider = {\n    options: {},\n\n    /**\n     * init is called when the provider is first loaded.\n     *\n     * options.providerOptions contains any operator configured parameters\n     * specified in the Consul agent config that is serving the UI.\n     *\n     * Consul will provide:\n     *\n     * 1. A boolean options.metrics_proxy_enabled to indicate whether the agent\n     * has a metrics proxy configured.\n     * 2. A fetch-like options.fetch which is a thin fetch wrapper that prefixes\n     * any url with the url of Consul's proxy endpoint and adds your current\n     * Consul ACL token to the request headers. Otherwise it functions like the\n     * browsers native fetch\n     *\n     * The provider should throw an Exception if the options are not valid for\n     * example because it requires a metrics proxy and one is not configured.\n     */\n    init: function (options) {\n      this.options = options;\n      if (!this.options.metrics_proxy_enabled) {\n        throw new Error(\n          'prometheus metrics provider currently requires the ui_config.metrics_proxy to be configured in the Consul agent.'\n        );\n      }\n    },\n\n    // simple httpGet function that also encodes query parameters\n    // before passing the constructed url through to native fetch\n    // any errors should throw an error with a statusCode property\n    httpGet: function (url, queryParams, headers) {\n      if (queryParams) {\n        var separator = url.indexOf('?') !== -1 ? '&' : '?';\n        var qs = Object.keys(queryParams)\n          .map(function (key) {\n            return encodeURIComponent(key) + '=' + encodeURIComponent(queryParams[key]);\n          })\n          .join('&');\n        url = url + separator + qs;\n      }\n      // fetch the url along with any headers\n      return this.options.fetch(url, { headers: headers || {} }).then(function (response) {\n        if (response.ok) {\n          return response.json();\n        } else {\n          // throw a statusCode error if any errors are received\n          var e = new Error('HTTP Error: ' + response.statusText);\n          e.statusCode = response.status;\n          throw e;\n        }\n      });\n    },\n\n    /**\n     * serviceRecentSummarySeries should return time series for a recent time\n     * period summarizing the usage of the named service in the indicated\n     * datacenter. In Consul Enterprise a non-empty namespace is also provided.\n     *\n     * If these metrics aren't available then an empty series array may be\n     * returned.\n     *\n     * The period may (later) be specified in options.startTime and\n     * options.endTime.\n     *\n     * The service's protocol must be given as one of Consul's supported\n     * protocols e.g. \"tcp\", \"http\", \"http2\", \"grpc\". If it is empty or the\n     * provider doesn't recognize the protocol, it should treat it as \"tcp\" and\n     * provide basic connection stats.\n     *\n     * The expected return value is a promise which resolves to an object that\n     * should look like the following:\n     *\n     *  {\n     *    // The unitSuffix is shown after the value in tooltips. Values will be\n     *    // rounded and shortened. Larger values will already have a suffix\n     *    // like \"10k\". The suffix provided here is concatenated directly\n     *    // allowing for suffixes like \"mbps/kbps\" by using a suffix of \"bps\".\n     *    // If the unit doesn't make sense in this format, include a\n     *    // leading space for example \" rps\" would show as \"1.2k rps\".\n     *    unitSuffix: \" rps\",\n     *\n     *    // The set of labels to graph. The key should exactly correspond to a\n     *    // property of every data point in the array below except for the\n     *    // special case \"Total\" which is used to show the sum of all the\n     *    // stacked graph values. The key is displayed in the tooltop so it\n     *    // should be human-friendly but as concise as possible. The value is a\n     *    // longer description that is displayed in the graph's key on request\n     *    // to explain exactly what the metrics mean.\n     *    labels: {\n     *      \"Total\": \"Total inbound requests per second.\",\n     *      \"Successes\": \"Successful responses (with an HTTP response code ...\",\n     *      \"Errors\": \"Error responses (with an HTTP response code in the ...\",\n     *    },\n     *\n     *    data: [\n     *      {\n     *        time: 1600944516286, // milliseconds since Unix epoch\n     *        \"Successes\": 1234.5,\n     *        \"Errors\": 2.3,\n     *      },\n     *      ...\n     *    ]\n     *  }\n     *\n     *  Every data point object should have a value for every series label\n     *  (except for \"Total\") otherwise it will be assumed to be \"0\".\n     */\n    serviceRecentSummarySeries: function (service, dc, nspace, protocol, options) {\n      // Fetch time-series\n      var series = [];\n      var labels = [];\n\n      // Set the start and end range here so that all queries end up with\n      // identical time axes. Later we might accept these as options.\n      var now = new Date().getTime() / 1000;\n      options.start = now - 15 * 60;\n      options.end = now;\n\n      if (this.hasL7Metrics(protocol)) {\n        return this.fetchRequestRateSeries(service, dc, nspace, options);\n      }\n\n      // Fallback to just L4 metrics.\n      return this.fetchDataRateSeries(service, dc, nspace, options);\n    },\n\n    /**\n     * serviceRecentSummaryStats should return four summary statistics for a\n     * recent time period for the named service in the indicated datacenter. In\n     * Consul Enterprise a non-empty namespace is also provided.\n     *\n     * If these metrics aren't available then an empty array may be returned.\n     *\n     * The period may (later) be specified in options.startTime and\n     * options.endTime.\n     *\n     * The service's protocol must be given as one of Consul's supported\n     * protocols e.g. \"tcp\", \"http\", \"http2\", \"grpc\". If it is empty or the\n     * provider doesn't recognize it it should treat it as \"tcp\" and provide\n     * just basic connection stats.\n     *\n     * The expected return value is a promise which resolves to an object that\n     * should look like the following:\n     *\n     *  {\n     *    stats: [ // We expect four of these for now.\n     *      {\n     *        // label should be 3 chars or fewer as an abbreviation\n     *        label: \"SR\",\n     *\n     *        // desc describes the stat in a tooltip\n     *        desc: \"Success Rate - the percentage of all requests that were not 5xx status\",\n     *\n     *        // value is a string allowing the provider to format it and add\n     *        // units as appropriate. It should be as compact as possible.\n     *        value: \"98%\",\n     *      }\n     *    ]\n     *  }\n     */\n    serviceRecentSummaryStats: function (service, dc, nspace, protocol, options) {\n      // Fetch stats\n      var stats = [];\n      if (this.hasL7Metrics(protocol)) {\n        stats.push(this.fetchRPS(service, dc, nspace, 'service', options));\n        stats.push(this.fetchER(service, dc, nspace, 'service', options));\n        stats.push(this.fetchPercentile(50, service, dc, nspace, 'service', options));\n        stats.push(this.fetchPercentile(99, service, dc, nspace, 'service', options));\n      } else {\n        // Fallback to just L4 metrics.\n        stats.push(this.fetchConnRate(service, dc, nspace, 'service', options));\n        stats.push(this.fetchServiceRx(service, dc, nspace, 'service', options));\n        stats.push(this.fetchServiceTx(service, dc, nspace, 'service', options));\n        stats.push(this.fetchServiceNoRoute(service, dc, nspace, 'service', options));\n      }\n      return this.fetchStats(stats);\n    },\n\n    /**\n     * upstreamRecentSummaryStats should return four summary statistics for each\n     * upstream service over a recent time period, relative to the named service\n     * in the indicated datacenter. In Consul Enterprise a non-empty namespace\n     * is also provided.\n     *\n     * Note that the upstreams themselves might be in different datacenters but\n     * we only pass the target service DC since typically these metrics should\n     * be from the outbound listener of the target service in this DC even if\n     * they eventually end up in another DC.\n     *\n     * If these metrics aren't available then an empty array may be returned.\n     *\n     * The period may (later) be specified in options.startTime and\n     * options.endTime.\n     *\n     * The expected return value format is shown below:\n     *\n     *   {\n     *     stats: {\n     *       // Each upstream will appear as an entry keyed by the upstream\n     *       // service name. The value is an array of stats with the same\n     *       // format as serviceRecentSummaryStats response.stats. Note that\n     *       // different upstreams might show different stats depending on\n     *       // their protocol.\n     *       \"upstream_name\": [\n     *         {label: \"SR\", desc: \"...\", value: \"99%\"},\n     *         ...\n     *       ],\n     *       ...\n     *     }\n     *   }\n     */\n    upstreamRecentSummaryStats: function (service, dc, nspace, options) {\n      return this.fetchRecentSummaryStats(service, dc, nspace, 'upstream', options);\n    },\n\n    /**\n     * downstreamRecentSummaryStats should return four summary statistics for\n     * each downstream service over a recent time period, relative to the named\n     * service in the indicated datacenter. In Consul Enterprise a non-empty\n     * namespace is also provided.\n     *\n     * Note that the service may have downstreams in different datacenters. For\n     * some metrics systems which are per-datacenter this makes it hard to query\n     * for all downstream metrics from one source. For now the UI will only show\n     * downstreams in the same datacenter as the target service. In the future\n     * this method may be called multiple times, once for each DC that contains\n     * downstream services to gather metrics from each. In that case a separate\n     * option for target datacenter will be used since the target service's DC\n     * is still needed to correctly identify the outbound clusters that will\n     * route to it from the remote DC.\n     *\n     * If these metrics aren't available then an empty array may be returned.\n     *\n     * The period may (later) be specified in options.startTime and\n     * options.endTime.\n     *\n     * The expected return value format is shown below:\n     *\n     *   {\n     *     stats: {\n     *       // Each downstream will appear as an entry keyed by \"service.namespace.dc\".\n     *       // The value is an array of stats with the same\n     *       // format as serviceRecentSummaryStats response.stats. Different\n     *       // downstreams may display different stats if required although the\n     *       // protocol should be the same for all as it is the target\n     *       // service's protocol that matters here.\n     *       \"web.default.dc1\": [\n     *         {label: \"SR\", desc: \"...\", value: \"99%\"},\n     *         ...\n     *       ],\n     *       ...\n     *     }\n     *   }\n     */\n    downstreamRecentSummaryStats: function (service, dc, nspace, options) {\n      return this.fetchRecentSummaryStats(service, dc, nspace, 'downstream', options);\n    },\n\n    fetchRecentSummaryStats: function (service, dc, nspace, type, options) {\n      // Fetch stats\n      var stats = [];\n\n      // We don't know which upstreams are HTTP/TCP so just fetch all of them.\n\n      // HTTP\n      stats.push(this.fetchRPS(service, dc, nspace, type, options));\n      stats.push(this.fetchER(service, dc, nspace, type, options));\n      stats.push(this.fetchPercentile(50, service, dc, nspace, type, options));\n      stats.push(this.fetchPercentile(99, service, dc, nspace, type, options));\n\n      // L4\n      stats.push(this.fetchConnRate(service, dc, nspace, type, options));\n      stats.push(this.fetchServiceRx(service, dc, nspace, type, options));\n      stats.push(this.fetchServiceTx(service, dc, nspace, type, options));\n      stats.push(this.fetchServiceNoRoute(service, dc, nspace, type, options));\n\n      return this.fetchStatsGrouped(stats);\n    },\n\n    hasL7Metrics: function (protocol) {\n      return protocol === 'http' || protocol === 'http2' || protocol === 'grpc';\n    },\n\n    fetchStats: function (statsPromises) {\n      var all = Promise.all(statsPromises).then(function (results) {\n        var data = {\n          stats: [],\n        };\n        // Add all non-empty stats\n        for (var i = 0; i < statsPromises.length; i++) {\n          if (results[i].value) {\n            data.stats.push(results[i]);\n          }\n        }\n        return data;\n      });\n\n      // Fetch the metrics async, and return a promise to the result.\n      return all;\n    },\n\n    fetchStatsGrouped: function (statsPromises) {\n      var all = Promise.all(statsPromises).then(function (results) {\n        var data = {\n          stats: {},\n        };\n        // Add all non-empty stats\n        for (var i = 0; i < statsPromises.length; i++) {\n          if (results[i]) {\n            for (var group in results[i]) {\n              if (!results[i].hasOwnProperty(group)) continue;\n              if (!data.stats[group]) {\n                data.stats[group] = [];\n              }\n              data.stats[group].push(results[i][group]);\n            }\n          }\n        }\n        return data;\n      });\n\n      // Fetch the metrics async, and return a promise to the result.\n      return all;\n    },\n\n    reformatSeries: function (unitSuffix, labelMap) {\n      return function (response) {\n        // Handle empty result sets gracefully.\n        if (\n          !response.data ||\n          !response.data.result ||\n          response.data.result.length == 0 ||\n          !response.data.result[0].values ||\n          response.data.result[0].values.length == 0\n        ) {\n          return emptySeries;\n        }\n        // Reformat the prometheus data to be the format we want with stacked\n        // values as object properties.\n\n        // Populate time values first based on first result since Prometheus will\n        // always return all the same points for all series in the query.\n        let series = response.data.result[0].values.map(function (d, i) {\n          return {\n            time: Math.round(d[0] * 1000),\n          };\n        });\n\n        // Then for each series returned populate the labels and values in the\n        // points.\n        response.data.result.map(function (d) {\n          d.values.map(function (p, i) {\n            series[i][d.metric.label] = parseFloat(p[1]);\n          });\n        });\n\n        return {\n          unitSuffix: unitSuffix,\n          labels: labelMap,\n          data: series,\n        };\n      };\n    },\n\n    fetchRequestRateSeries: function (service, dc, nspace, options) {\n      // We need the sum of all non-500 error rates as one value and the 500\n      // error rate as a separate series so that they stack to show the full\n      // request rate. Some creative label replacement makes this possible in\n      // one query.\n      var q =\n        `sum by (label) (` +\n        // The outer label_replace catches 5xx error and relabels them as\n        // err=yes\n        `label_replace(` +\n        // The inner label_replace relabels all !5xx rates as err=no so they\n        // will get summed together.\n        `label_replace(` +\n        // Get rate of requests to the service\n        `irate(envoy_listener_http_downstream_rq_xx{` +\n        `consul_source_service=\"${service}\",` +\n        `consul_source_datacenter=\"${dc}\",` +\n        `consul_source_namespace=\"${nspace}\",` +\n        `envoy_http_conn_manager_prefix=\"public_listener\"}[10m])` +\n        // ... inner replacement matches all code classes except \"5\" and\n        // applies err=no\n        `, \"label\", \"Successes\", \"envoy_response_code_class\", \"[^5]\")` +\n        // ... outer replacement matches code=5 and applies err=yes\n        `, \"label\", \"Errors\", \"envoy_response_code_class\", \"5\")` +\n        `)`;\n      var labelMap = {\n        Total: 'Total inbound requests per second',\n        Successes:\n          'Successful responses (with an HTTP response code not in the 5xx range) per second.',\n        Errors: 'Error responses (with an HTTP response code in the 5xx range) per second.',\n      };\n      return this.fetchSeries(q, options).then(this.reformatSeries(' rps', labelMap));\n    },\n\n    fetchDataRateSeries: function (service, dc, nspace, options) {\n      // 8 * converts from bytes/second to bits/second\n      var q =\n        `8 * sum by (label) (` +\n        // Label replace generates a unique label per rx/tx metric to stop them\n        // being summed together.\n        `label_replace(` +\n        // Get the tx rate\n        `irate(envoy_tcp_downstream_cx_tx_bytes_total{` +\n        `consul_source_service=\"${service}\",` +\n        `consul_source_datacenter=\"${dc}\",` +\n        `consul_source_namespace=\"${nspace}\",` +\n        `envoy_tcp_prefix=\"public_listener\"}[10m])` +\n        // Match all and apply the tx label\n        `, \"label\", \"Outbound\", \"__name__\", \".*\"` +\n        // Union those vectors with the RX ones\n        `) or label_replace(` +\n        // Get the rx rate\n        `irate(envoy_tcp_downstream_cx_rx_bytes_total{` +\n        `consul_source_service=\"${service}\",` +\n        `consul_source_datacenter=\"${dc}\",` +\n        `consul_source_namespace=\"${nspace}\",` +\n        `envoy_tcp_prefix=\"public_listener\"}[10m])` +\n        // Match all and apply the rx label\n        `, \"label\", \"Inbound\", \"__name__\", \".*\"` +\n        `)` +\n        `)`;\n      var labelMap = {\n        Total: 'Total bandwidth',\n        Inbound: 'Inbound data rate (data recieved) from the network in bits per second.',\n        Outbound: 'Outbound data rate (data transmitted) from the network in bits per second.',\n      };\n      return this.fetchSeries(q, options).then(this.reformatSeries('bps', labelMap));\n    },\n\n    makeSubject: function (service, dc, nspace, type) {\n      var entity = `${nspace}/${service} (${dc})`;\n      if (type == 'upstream') {\n        // {{GROUP}} is a placeholder that is replaced by the upstream name\n        return `${entity} &rarr; {{GROUP}}`;\n      }\n      if (type == 'downstream') {\n        // {{GROUP}} is a placeholder that is replaced by the downstream name\n        return `{{GROUP}} &rarr; ${entity}`;\n      }\n      return entity;\n    },\n\n    makeHTTPSelector: function (service, dc, nspace, type) {\n      // Downstreams are totally different\n      if (type == 'downstream') {\n        return `consul_destination_service=\"${service}\",consul_destination_datacenter=\"${dc}\",consul_destination_namespace=\"${nspace}\"`;\n      }\n      var lc = `consul_source_service=\"${service}\",consul_source_datacenter=\"${dc}\",consul_source_namespace=\"${nspace}\"`;\n      if (type == 'upstream') {\n        lc += `,envoy_http_conn_manager_prefix=\"upstream\"`;\n      } else {\n        // Only care about inbound public listener\n        lc += `,envoy_http_conn_manager_prefix=\"public_listener\"`;\n      }\n      return lc;\n    },\n\n    makeTCPSelector: function (service, dc, nspace, type) {\n      // Downstreams are totally different\n      if (type == 'downstream') {\n        return `consul_destination_service=\"${service}\",consul_destination_datacenter=\"${dc}\",consul_destination_namespace=\"${nspace}\"`;\n      }\n      var lc = `consul_source_service=\"${service}\",consul_source_datacenter=\"${dc}\",consul_source_namespace=\"${nspace}\"`;\n      if (type == 'upstream') {\n        lc += `,envoy_tcp_prefix=~\"upstream.*\"`;\n      } else {\n        // Only care about inbound public listener\n        lc += `,envoy_tcp_prefix=\"public_listener\"`;\n      }\n      return lc;\n    },\n\n    groupQuery: function (type, q) {\n      if (type == 'upstream') {\n        q += ' by (consul_upstream_service,consul_upstream_datacenter,consul_upstream_namespace)';\n      } else if (type == 'downstream') {\n        q += ' by (consul_source_service,consul_source_datacenter,consul_source_namespace)';\n      }\n      return q;\n    },\n\n    groupByInfix: function (type) {\n      if (type == 'upstream') {\n        return 'upstream';\n      } else if (type == 'downstream') {\n        return 'source';\n      } else {\n        return false;\n      }\n    },\n\n    metricPrefixHTTP: function (type) {\n      if (type == 'downstream') {\n        return 'envoy_cluster_upstream_rq';\n      }\n      return 'envoy_http_downstream_rq';\n    },\n\n    metricPrefixTCP: function (type) {\n      if (type == 'downstream') {\n        return 'envoy_cluster_upstream_cx';\n      }\n      return 'envoy_tcp_downstream_cx';\n    },\n\n    fetchRPS: function (service, dc, nspace, type, options) {\n      var sel = this.makeHTTPSelector(service, dc, nspace, type);\n      var subject = this.makeSubject(service, dc, nspace, type);\n      var metricPfx = this.metricPrefixHTTP(type);\n      var q = `sum(rate(${metricPfx}_completed{${sel}}[15m]))`;\n      return this.fetchStat(\n        this.groupQuery(type, q),\n        'RPS',\n        `<b>${subject}</b> request rate averaged over the last 15 minutes`,\n        shortNumStr,\n        this.groupByInfix(type)\n      );\n    },\n\n    fetchER: function (service, dc, nspace, type, options) {\n      var sel = this.makeHTTPSelector(service, dc, nspace, type);\n      var subject = this.makeSubject(service, dc, nspace, type);\n      var groupBy = '';\n      if (type == 'upstream') {\n        groupBy +=\n          ' by (consul_upstream_service,consul_upstream_datacenter,consul_upstream_namespace)';\n      } else if (type == 'downstream') {\n        groupBy += ' by (consul_source_service,consul_source_datacenter,consul_source_namespace)';\n      }\n      var metricPfx = this.metricPrefixHTTP(type);\n      var q = `sum(rate(${metricPfx}_xx{${sel},envoy_response_code_class=\"5\"}[15m]))${groupBy}/sum(rate(${metricPfx}_xx{${sel}}[15m]))${groupBy}`;\n      return this.fetchStat(\n        q,\n        'ER',\n        `Percentage of <b>${subject}</b> requests which were 5xx status over the last 15 minutes`,\n        function (val) {\n          return shortNumStr(val) + '%';\n        },\n        this.groupByInfix(type)\n      );\n    },\n\n    fetchPercentile: function (percentile, service, dc, nspace, type, options) {\n      var sel = this.makeHTTPSelector(service, dc, nspace, type);\n      var subject = this.makeSubject(service, dc, nspace, type);\n      var groupBy = 'le';\n      if (type == 'upstream') {\n        groupBy += ',consul_upstream_service,consul_upstream_datacenter,consul_upstream_namespace';\n      } else if (type == 'downstream') {\n        groupBy += ',consul_source_service,consul_source_datacenter,consul_source_namespace';\n      }\n      var metricPfx = this.metricPrefixHTTP(type);\n      var q = `histogram_quantile(${percentile /\n        100}, sum by(${groupBy}) (rate(${metricPfx}_time_bucket{${sel}}[15m])))`;\n      return this.fetchStat(\n        q,\n        `P${percentile}`,\n        `<b>${subject}</b> ${percentile}th percentile request service time over the last 15 minutes`,\n        shortTimeStr,\n        this.groupByInfix(type)\n      );\n    },\n\n    fetchConnRate: function (service, dc, nspace, type, options) {\n      var sel = this.makeTCPSelector(service, dc, nspace, type);\n      var subject = this.makeSubject(service, dc, nspace, type);\n      var metricPfx = this.metricPrefixTCP(type);\n      var q = `sum(rate(${metricPfx}_total{${sel}}[15m]))`;\n      return this.fetchStat(\n        this.groupQuery(type, q),\n        'CR',\n        `<b>${subject}</b> inbound TCP connections per second averaged over the last 15 minutes`,\n        shortNumStr,\n        this.groupByInfix(type)\n      );\n    },\n\n    fetchServiceRx: function (service, dc, nspace, type, options) {\n      var sel = this.makeTCPSelector(service, dc, nspace, type);\n      var subject = this.makeSubject(service, dc, nspace, type);\n      var metricPfx = this.metricPrefixTCP(type);\n      var q = `8 * sum(rate(${metricPfx}_rx_bytes_total{${sel}}[15m]))`;\n      return this.fetchStat(\n        this.groupQuery(type, q),\n        'RX',\n        `<b>${subject}</b> received bits per second averaged over the last 15 minutes`,\n        shortNumStr,\n        this.groupByInfix(type)\n      );\n    },\n\n    fetchServiceTx: function (service, dc, nspace, type, options) {\n      var sel = this.makeTCPSelector(service, dc, nspace, type);\n      var subject = this.makeSubject(service, dc, nspace, type);\n      var metricPfx = this.metricPrefixTCP(type);\n      var q = `8 * sum(rate(${metricPfx}_tx_bytes_total{${sel}}[15m]))`;\n      var self = this;\n      return this.fetchStat(\n        this.groupQuery(type, q),\n        'TX',\n        `<b>${subject}</b> transmitted bits per second averaged over the last 15 minutes`,\n        shortNumStr,\n        this.groupByInfix(type)\n      );\n    },\n\n    fetchServiceNoRoute: function (service, dc, nspace, type, options) {\n      var sel = this.makeTCPSelector(service, dc, nspace, type);\n      var subject = this.makeSubject(service, dc, nspace, type);\n      var metricPfx = this.metricPrefixTCP(type);\n      var metric = '_no_route';\n      if (type == 'downstream') {\n        metric = '_connect_fail';\n      }\n      var q = `sum(rate(${metricPfx}${metric}{${sel}}[15m]))`;\n      return this.fetchStat(\n        this.groupQuery(type, q),\n        'NR',\n        `<b>${subject}</b> unroutable (failed) connections per second averaged over the last 15 minutes`,\n        shortNumStr,\n        this.groupByInfix(type)\n      );\n    },\n\n    fetchStat: function (promql, label, desc, formatter, groupByInfix) {\n      if (!groupByInfix) {\n        // If we don't have a grouped result and its just a single stat, return\n        // no result as a zero not a missing stat.\n        promql += ' OR on() vector(0)';\n      }\n      var params = {\n        query: promql,\n        time: new Date().getTime() / 1000,\n      };\n      return this.httpGet('/api/v1/query', params).then(function (response) {\n        if (!groupByInfix) {\n          // Not grouped, expect just one stat value return that\n          var v = parseFloat(response.data.result[0].value[1]);\n          return {\n            label: label,\n            desc: desc,\n            value: isNaN(v) ? '-' : formatter(v),\n          };\n        }\n\n        var data = {};\n        for (var i = 0; i < response.data.result.length; i++) {\n          var res = response.data.result[i];\n          var v = parseFloat(res.value[1]);\n          var service = res.metric['consul_' + groupByInfix + '_service'];\n          var nspace = res.metric['consul_' + groupByInfix + '_namespace'];\n          var datacenter = res.metric['consul_' + groupByInfix + '_datacenter'];\n          var groupName = `${service}.${nspace}.${datacenter}`;\n          data[groupName] = {\n            label: label,\n            desc: desc.replace('{{GROUP}}', groupName),\n            value: isNaN(v) ? '-' : formatter(v),\n          };\n        }\n        return data;\n      });\n    },\n\n    fetchSeries: function (promql, options) {\n      var params = {\n        query: promql,\n        start: options.start,\n        end: options.end,\n        step: '10s',\n        timeout: '8s',\n      };\n      return this.httpGet('/api/v1/query_range', params);\n    },\n  };\n\n  // Helper functions\n  function shortNumStr(n) {\n    if (n < 1e3) {\n      if (Number.isInteger(n)) return '' + n;\n      if (n >= 100) {\n        // Go to 3 significant figures but wrap it in Number to avoid scientific\n        // notation lie 2.3e+2 for 230.\n        return Number(n.toPrecision(3));\n      }\n      if (n < 1) {\n        // Very small numbers show with limited precision to prevent long string\n        // of 0.000000.\n        return Number(n.toFixed(2));\n      } else {\n        // Two sig figs is enough below this\n        return Number(n.toPrecision(2));\n      }\n    }\n    if (n >= 1e3 && n < 1e6) return +(n / 1e3).toPrecision(3) + 'k';\n    if (n >= 1e6 && n < 1e9) return +(n / 1e6).toPrecision(3) + 'm';\n    if (n >= 1e9 && n < 1e12) return +(n / 1e9).toPrecision(3) + 'g';\n    if (n >= 1e12) return +(n / 1e12).toFixed(0) + 't';\n  }\n\n  function shortTimeStr(n) {\n    if (n < 1e3) return Math.round(n) + 'ms';\n\n    var secs = n / 1e3;\n    if (secs < 60) return secs.toFixed(1) + 's';\n\n    var mins = secs / 60;\n    if (mins < 60) return mins.toFixed(1) + 'm';\n\n    var hours = mins / 60;\n    if (hours < 24) return hours.toFixed(1) + 'h';\n\n    var days = hours / 24;\n    return days.toFixed(1) + 'd';\n  }\n\n  /* global consul:writable */\n  window.consul.registerMetricsProvider('prometheus', prometheusProvider);\n})();\n"],"names":[],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;","file":"prometheus.js"}