Explorar o código

(prometheus) add query result template query

Mitsuhiro Tanda %!s(int64=10) %!d(string=hai) anos
pai
achega
2cce057df1

+ 1 - 0
docs/sources/datasources/prometheus.md

@@ -48,6 +48,7 @@ Name | Description
 `label_values(label)` | Returns a list of label values for the `label` in every metric.
 `label_values(metric, label)` | Returns a list of label values for the `label` in the specified metric.
 `metrics(metric)` | Returns a list of metrics matching the specified `metric` regex.
+`query_result(query)` | Returns a list of Prometheus query result for the `query`.
 
 For details of `metric names` & `label names`, and `label values`, please refer to the [Prometheus documentation](http://prometheus.io/docs/concepts/data_model/#metric-names-and-labels).
 

+ 19 - 78
public/app/plugins/datasource/prometheus/datasource.ts

@@ -5,6 +5,7 @@ import _ from 'lodash';
 import moment from 'moment';
 
 import * as dateMath from 'app/core/utils/datemath';
+import PrometheusMetricFindQuery from './metric_find_query';
 
 var durationSplitRegexp = /(\d+)(ms|s|m|h|d|w|M|y)/;
 
@@ -90,7 +91,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
         delete self.lastErrors.query;
 
         _.each(response.data.data.result, function(metricData) {
-          result.push(transformMetricData(metricData, options.targets[index], start, end));
+          result.push(self.transformMetricData(metricData, options.targets[index], start, end));
         });
       });
 
@@ -123,69 +124,8 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
       return $q.reject(err);
     }
 
-    var label_values_regex = /^label_values\(([^,]+)(?:,\s*(.+))?\)$/;
-    var metric_names_regex = /^metrics\((.+)\)$/;
-
-    var url;
-    var label_values_query = interpolated.match(label_values_regex);
-    if (label_values_query) {
-      if (!label_values_query[2]) {
-        // return label values globally
-        url = '/api/v1/label/' + label_values_query[1] + '/values';
-
-        return this._request('GET', url).then(function(result) {
-          return _.map(result.data.data, function(value) {
-            return {text: value};
-          });
-        });
-      } else {
-        url = '/api/v1/series?match[]=' + encodeURIComponent(label_values_query[1]);
-
-        return this._request('GET', url)
-        .then(function(result) {
-          return _.map(result.data.data, function(metric) {
-            return {
-              text: metric[label_values_query[2]],
-              expandable: true
-            };
-          });
-        });
-      }
-    }
-
-    var metric_names_query = interpolated.match(metric_names_regex);
-    if (metric_names_query) {
-      url = '/api/v1/label/__name__/values';
-
-      return this._request('GET', url)
-      .then(function(result) {
-        return _.chain(result.data.data)
-        .filter(function(metricName) {
-          var r = new RegExp(metric_names_query[1]);
-          return r.test(metricName);
-        })
-        .map(function(matchedMetricName) {
-          return {
-            text: matchedMetricName,
-            expandable: true
-          };
-        })
-        .value();
-      });
-    } else {
-      // if query contains full metric name, return metric name and label list
-      url = '/api/v1/series?match[]=' + encodeURIComponent(interpolated);
-
-      return this._request('GET', url)
-      .then(function(result) {
-        return _.map(result.data.data, function(metric) {
-          return {
-            text: getOriginalMetricName(metric),
-            expandable: true
-          };
-        });
-      });
-    }
+    var metricFindQuery = new PrometheusMetricFindQuery(this, interpolated);
+    return metricFindQuery.process();
   };
 
   this.annotationQuery = function(options) {
@@ -210,6 +150,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
     };
     var start = getPrometheusTime(options.range.from, false);
     var end = getPrometheusTime(options.range.to, true);
+    var self = this;
     return this.performTimeSeriesQuery(query, start, end).then(function(results) {
       var eventList = [];
       tagKeys = tagKeys.split(',');
@@ -225,9 +166,9 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
             var event = {
               annotation: annotation,
               time: Math.floor(value[0]) * 1000,
-              title: renderTemplate(titleFormat, series.metric),
+              title: self.renderTemplate(titleFormat, series.metric),
               tags: tags,
-              text: renderTemplate(textFormat, series.metric)
+              text: self.renderTemplate(textFormat, series.metric)
             };
 
             eventList.push(event);
@@ -245,7 +186,7 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
     });
   };
 
-  PrometheusDatasource.prototype.calculateInterval = function(interval, intervalFactor) {
+  this.calculateInterval = function(interval, intervalFactor) {
     var m = interval.match(durationSplitRegexp);
     var dur = moment.duration(parseInt(m[1]), m[2]);
     var sec = dur.asSeconds();
@@ -256,11 +197,11 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
     return Math.ceil(sec * intervalFactor);
   };
 
-  function transformMetricData(md, options, start, end) {
+  this.transformMetricData = function(md, options, start, end) {
     var dps = [],
       metricLabel = null;
 
-    metricLabel = createMetricLabel(md.metric, options);
+    metricLabel = this.createMetricLabel(md.metric, options);
 
     var stepMs = parseInt(options.step) * 1000;
     var baseTimestamp = start * 1000;
@@ -284,17 +225,17 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
     }
 
     return { target: metricLabel, datapoints: dps };
-  }
+  };
 
-  function createMetricLabel(labelData, options) {
+  this.createMetricLabel = function(labelData, options) {
     if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
-      return getOriginalMetricName(labelData);
+      return this.getOriginalMetricName(labelData);
     }
 
-    return renderTemplate(options.legendFormat, labelData) || '{}';
-  }
+    return this.renderTemplate(options.legendFormat, labelData) || '{}';
+  };
 
-  function renderTemplate(format, data) {
+  this.renderTemplate = function(format, data) {
     var originalSettings = _.templateSettings;
     _.templateSettings = {
       interpolate: /\{\{(.+?)\}\}/g
@@ -311,16 +252,16 @@ export function PrometheusDatasource(instanceSettings, $q, backendSrv, templateS
     _.templateSettings = originalSettings;
 
     return result;
-  }
+  };
 
-  function getOriginalMetricName(labelData) {
+  this.getOriginalMetricName = function(labelData) {
     var metricName = labelData.__name__ || '';
     delete labelData.__name__;
     var labelPart = _.map(_.pairs(labelData), function(label) {
       return label[0] + '="' + label[1] + '"';
     }).join(',');
     return metricName + '{' + labelPart + '}';
-  }
+  };
 
   function getPrometheusTime(date, roundUp): number {
     if (_.isString(date)) {

+ 2 - 0
public/app/plugins/datasource/prometheus/metric_find_query.d.ts

@@ -0,0 +1,2 @@
+declare var test: any;
+export default test;

+ 125 - 0
public/app/plugins/datasource/prometheus/metric_find_query.js

@@ -0,0 +1,125 @@
+define([
+  'lodash',
+  'moment',
+],
+function (_, moment) {
+  'use strict';
+
+  function PrometheusMetricFindQuery(datasource, query) {
+    this.datasource = datasource;
+    this.query = query;
+  }
+
+  PrometheusMetricFindQuery.prototype.process = function() {
+    var label_values_regex = /^label_values\(([^,]+)(?:,\s*(.+))?\)$/;
+    var metric_names_regex = /^metrics\((.+)\)$/;
+    var query_result_regex = /^query_result\((.+)\)$/;
+
+    var label_values_query = this.query.match(label_values_regex);
+    if (label_values_query) {
+      if (label_values_query[2]) {
+        return this.labelValuesQuery(label_values_query[2], label_values_query[1]);
+      } else {
+        return this.labelValuesQuery(label_values_query[1], null);
+      }
+    }
+
+    var metric_names_query = this.query.match(metric_names_regex);
+    if (metric_names_query) {
+      return this.metricNameQuery(metric_names_query[1]);
+    }
+
+    var query_result_query = this.query.match(query_result_regex);
+    if (query_result_query) {
+      return this.queryResultQuery(query_result_query[1]);
+    }
+
+    // if query contains full metric name, return metric name and label list
+    return this.metricNameAndLabelsQuery(this.query);
+  };
+
+  PrometheusMetricFindQuery.prototype.labelValuesQuery = function(label, metric) {
+    var url;
+
+    if (!metric) {
+      // return label values globally
+      url = '/api/v1/label/' + label + '/values';
+
+      return this.datasource._request('GET', url).then(function(result) {
+        return _.map(result.data.data, function(value) {
+          return {text: value};
+        });
+      });
+    } else {
+      url = '/api/v1/series?match[]=' + encodeURIComponent(metric);
+
+      return this.datasource._request('GET', url)
+      .then(function(result) {
+        return _.map(result.data.data, function(metric) {
+          return {
+            text: metric[label],
+            expandable: true
+          };
+        });
+      });
+    }
+  };
+
+  PrometheusMetricFindQuery.prototype.metricNameQuery = function(metricFilterPattern) {
+    var url = '/api/v1/label/__name__/values';
+
+    return this.datasource._request('GET', url)
+    .then(function(result) {
+      return _.chain(result.data.data)
+      .filter(function(metricName) {
+        var r = new RegExp(metricFilterPattern);
+        return r.test(metricName);
+      })
+      .map(function(matchedMetricName) {
+        return {
+          text: matchedMetricName,
+          expandable: true
+        };
+      })
+      .value();
+    });
+  };
+
+  PrometheusMetricFindQuery.prototype.queryResultQuery = function(query) {
+    var url = '/api/v1/query?query=' + encodeURIComponent(query) + '&time=' + (moment().valueOf() / 1000);
+
+    return this.datasource._request('GET', url)
+    .then(function(result) {
+      return _.map(result.data.data.result, function(metricData) {
+        var text = metricData.metric.__name__ || '';
+        delete metricData.metric.__name__;
+        text += '{' +
+                _.map(metricData.metric, function(v, k) { return k + '="' + v + '"'; }).join(',') +
+                '}';
+        text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000;
+
+        return {
+          text: text,
+          expandable: true
+        };
+      });
+    });
+  };
+
+  PrometheusMetricFindQuery.prototype.metricNameAndLabelsQuery = function(query) {
+    var url = '/api/v1/series?match[]=' + encodeURIComponent(query);
+
+    var self = this;
+    return this.datasource._request('GET', url)
+    .then(function(result) {
+      return _.map(result.data.data, function(metric) {
+        return {
+          text: self.datasource.getOriginalMetricName(metric),
+          expandable: true
+        };
+      });
+    });
+  };
+
+  return PrometheusMetricFindQuery;
+});

+ 0 - 41
public/app/plugins/datasource/prometheus/specs/datasource_specs.ts

@@ -116,47 +116,6 @@ describe('PrometheusDatasource', function() {
       expect(results.data[1].datapoints[3][0]).to.be(null);
     });
   });
-  describe('When performing metricFindQuery', function() {
-    var results;
-    var response;
-    it('label_values(resource) should generate label search query', function() {
-      response = {
-        status: "success",
-        data: ["value1", "value2", "value3"]
-      };
-      ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/resource/values').respond(response);
-      ctx.ds.metricFindQuery('label_values(resource)').then(function(data) { results = data; });
-      ctx.$httpBackend.flush();
-      ctx.$rootScope.$apply();
-      expect(results.length).to.be(3);
-    });
-    it('label_values(metric, resource) should generate series query', function() {
-      response = {
-        status: "success",
-        data: [
-          {__name__: "metric", resource: "value1"},
-          {__name__: "metric", resource: "value2"},
-          {__name__: "metric", resource: "value3"}
-        ]
-      };
-      ctx.$httpBackend.expect('GET', 'proxied/api/v1/series?match[]=metric').respond(response);
-      ctx.ds.metricFindQuery('label_values(metric, resource)').then(function(data) { results = data; });
-      ctx.$httpBackend.flush();
-      ctx.$rootScope.$apply();
-      expect(results.length).to.be(3);
-    });
-    it('metrics(metric.*) should generate metric name query', function() {
-      response = {
-        status: "success",
-        data: ["metric1","metric2","metric3","nomatch"]
-      };
-      ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/__name__/values').respond(response);
-      ctx.ds.metricFindQuery('metrics(metric.*)').then(function(data) { results = data; });
-      ctx.$httpBackend.flush();
-      ctx.$rootScope.$apply();
-      expect(results.length).to.be(3);
-    });
-  });
   describe('When performing annotationQuery', function() {
     var results;
     var urlExpected = 'proxied/api/v1/query_range?query=' +

+ 83 - 0
public/app/plugins/datasource/prometheus/specs/metric_find_query_specs.ts

@@ -0,0 +1,83 @@
+import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common';
+import moment from 'moment';
+import helpers from 'test/specs/helpers';
+import {PrometheusDatasource} from '../datasource';
+import PrometheusMetricFindQuery from '../metric_find_query';
+
+describe('PrometheusMetricFindQuery', function() {
+
+  var ctx = new helpers.ServiceTestContext();
+  var instanceSettings = {url: 'proxied', directUrl: 'direct', user: 'test', password: 'mupp' };
+  beforeEach(angularMocks.module('grafana.core'));
+  beforeEach(angularMocks.module('grafana.services'));
+  beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
+    ctx.$q = $q;
+    ctx.$httpBackend =  $httpBackend;
+    ctx.$rootScope = $rootScope;
+    ctx.ds = $injector.instantiate(PrometheusDatasource, {instanceSettings: instanceSettings});
+  }));
+
+  describe('When performing metricFindQuery', function() {
+    var results;
+    var response;
+    it('label_values(resource) should generate label search query', function() {
+      response = {
+        status: "success",
+        data: ["value1", "value2", "value3"]
+      };
+      ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/resource/values').respond(response);
+      var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(resource)');
+      pm.process().then(function(data) { results = data; });
+      ctx.$httpBackend.flush();
+      ctx.$rootScope.$apply();
+      expect(results.length).to.be(3);
+    });
+    it('label_values(metric, resource) should generate series query', function() {
+      response = {
+        status: "success",
+        data: [
+          {__name__: "metric", resource: "value1"},
+          {__name__: "metric", resource: "value2"},
+          {__name__: "metric", resource: "value3"}
+        ]
+      };
+      ctx.$httpBackend.expect('GET', 'proxied/api/v1/series?match[]=metric').respond(response);
+      var pm = new PrometheusMetricFindQuery(ctx.ds, 'label_values(metric, resource)');
+      pm.process().then(function(data) { results = data; });
+      ctx.$httpBackend.flush();
+      ctx.$rootScope.$apply();
+      expect(results.length).to.be(3);
+    });
+    it('metrics(metric.*) should generate metric name query', function() {
+      response = {
+        status: "success",
+        data: ["metric1","metric2","metric3","nomatch"]
+      };
+      ctx.$httpBackend.expect('GET', 'proxied/api/v1/label/__name__/values').respond(response);
+      var pm = new PrometheusMetricFindQuery(ctx.ds, 'metrics(metric.*)');
+      pm.process().then(function(data) { results = data; });
+      ctx.$httpBackend.flush();
+      ctx.$rootScope.$apply();
+      expect(results.length).to.be(3);
+    });
+    it('query_result(metric) should generate metric name query', function() {
+      response = {
+        status: "success",
+        data: {
+          resultType: "vector",
+          result: [{
+            metric: {"__name__": "metric", job: "testjob"},
+            value: [1443454528.000, "3846"]
+          }]
+        }
+      };
+      ctx.$httpBackend.expect('GET', /proxied\/api\/v1\/query\?query=metric&time=.*/).respond(response);
+      var pm = new PrometheusMetricFindQuery(ctx.ds, 'query_result(metric)');
+      pm.process().then(function(data) { results = data; });
+      ctx.$httpBackend.flush();
+      ctx.$rootScope.$apply();
+      expect(results.length).to.be(1);
+      expect(results[0].text).to.be('metric{job="testjob"} 3846 1443454528000');
+    });
+  });
+});