More efficient JSON query result format.

This depends on https://github.com/prometheus/client_golang/pull/51.

For vectors, the result format looks like this:

```json
{
   "version": 1,
   "type" : "vector",
   "value" : [
      {
         "timestamp" : 1421765411.045,
         "value" : "65.475000",
         "metric" : {
            "quantile" : "0.5",
            "instance" : "http://localhost:9090/metrics",
            "job" : "prometheus",
            "__name__" : "http_request_duration_microseconds",
            "handler" : "/static/",
            "method" : "get",
            "code" : "304"
         }
      },
      {
         "timestamp" : 1421765411.045,
         "value" : "5826.339000",
         "metric" : {
            "quantile" : "0.9",
            "instance" : "http://localhost:9090/metrics",
            "job" : "prometheus",
            "__name__" : "http_request_duration_microseconds",
            "handler" : "prometheus",
            "method" : "get",
            "code" : "200"
         }
      },
      /* ... */
   ]
}
```

For matrices, it looks like this:

```json
{
   "version": 1,
   "type" : "matrix",
   "value" : [
      {
         "metric" : {
            "quantile" : "0.99",
            "instance" : "http://localhost:9090/metrics",
            "job" : "prometheus",
            "__name__" : "http_request_duration_microseconds",
            "handler" : "/static/",
            "method" : "get",
            "code" : "200"
         },
         "values" : [
            [
               1421765547.659,
               "29162.953000"
            ],
            [
               1421765548.659,
               "29162.953000"
            ],
            [
               1421765549.659,
               "29162.953000"
            ],
            /* ... */
         ]
      }
   ]
}
```
This commit is contained in:
Julius Volz 2015-01-20 15:13:49 +01:00
parent f7e3da5488
commit d4374a9265
6 changed files with 51 additions and 44 deletions

View file

@ -81,7 +81,7 @@ func TestNotificationHandler(t *testing.T) {
// Correct message. // Correct message.
summary: "Summary", summary: "Summary",
description: "Description", description: "Description",
message: `[{"Description":"Description","Labels":{"instance":"testinstance"},"Payload":{"ActiveSince":"0001-01-01T00:00:00Z","AlertingRule":"Test rule string","GeneratorURL":"prometheus_url","Value":"0.333333"},"Summary":"Summary"}]`, message: `[{"Description":"Description","Labels":{"instance":"testinstance"},"Payload":{"ActiveSince":"0001-01-01T00:00:00Z","AlertingRule":"Test rule string","GeneratorURL":"prometheus_url","Value":"0.3333333333333333"},"Summary":"Summary"}]`,
}, },
} }

View file

@ -36,15 +36,15 @@ var stalenessDelta = flag.Duration("query.staleness-delta", 300*time.Second, "St
// SampleStream is a stream of Values belonging to an attached COWMetric. // SampleStream is a stream of Values belonging to an attached COWMetric.
type SampleStream struct { type SampleStream struct {
Metric clientmodel.COWMetric Metric clientmodel.COWMetric `json:"metric"`
Values metric.Values Values metric.Values `json:"values"`
} }
// Sample is a single sample belonging to a COWMetric. // Sample is a single sample belonging to a COWMetric.
type Sample struct { type Sample struct {
Metric clientmodel.COWMetric Metric clientmodel.COWMetric `json:"metric"`
Value clientmodel.SampleValue Value clientmodel.SampleValue `json:"value"`
Timestamp clientmodel.Timestamp Timestamp clientmodel.Timestamp `json:"timestamp"`
} }
// Vector is basically only an alias for clientmodel.Samples, but the // Vector is basically only an alias for clientmodel.Samples, but the

View file

@ -37,6 +37,8 @@ const (
JSON JSON
) )
const jsonFormatVersion = 1
func (opType BinOpType) String() string { func (opType BinOpType) String() string {
opTypeMap := map[BinOpType]string{ opTypeMap := map[BinOpType]string{
Add: "+", Add: "+",
@ -121,11 +123,13 @@ func (matrix Matrix) String() string {
// ErrorToJSON converts the given error into JSON. // ErrorToJSON converts the given error into JSON.
func ErrorToJSON(err error) string { func ErrorToJSON(err error) string {
errorStruct := struct { errorStruct := struct {
Type string Type string `json:"type"`
Value string Value string `json:"value"`
Version int `json:"version"`
}{ }{
Type: "error", Type: "error",
Value: err.Error(), Value: err.Error(),
Version: jsonFormatVersion,
} }
errorJSON, err := json.Marshal(errorStruct) errorJSON, err := json.Marshal(errorStruct)
@ -139,11 +143,13 @@ func ErrorToJSON(err error) string {
// 'vector', or 'matrix' into its JSON representation. // 'vector', or 'matrix' into its JSON representation.
func TypedValueToJSON(data interface{}, typeStr string) string { func TypedValueToJSON(data interface{}, typeStr string) string {
dataStruct := struct { dataStruct := struct {
Type string Type string `json:"type"`
Value interface{} Value interface{} `json:"value"`
Version int `json:"version"`
}{ }{
Type: typeStr, Type: typeStr,
Value: data, Value: data,
Version: jsonFormatVersion,
} }
dataJSON, err := json.Marshal(dataStruct) dataJSON, err := json.Marshal(dataStruct)
if err != nil { if err != nil {

View file

@ -15,13 +15,14 @@ package metric
import ( import (
"fmt" "fmt"
"strconv"
clientmodel "github.com/prometheus/client_golang/model" clientmodel "github.com/prometheus/client_golang/model"
) )
// MarshalJSON implements json.Marshaler. // MarshalJSON implements json.Marshaler.
func (s SamplePair) MarshalJSON() ([]byte, error) { func (s SamplePair) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("{\"Value\": \"%f\", \"Timestamp\": %s}", s.Value, s.Timestamp.String())), nil return []byte(fmt.Sprintf("[%s, \"%s\"]", s.Timestamp.String(), strconv.FormatFloat(float64(s.Value), 'f', -1, 64))), nil
} }
// SamplePair pairs a SampleValue with a Timestamp. // SamplePair pairs a SampleValue with a Timestamp.

View file

@ -385,18 +385,18 @@ Prometheus.Graph.prototype.parseValue = function(value) {
Prometheus.Graph.prototype.transformData = function(json) { Prometheus.Graph.prototype.transformData = function(json) {
self = this; self = this;
var palette = new Rickshaw.Color.Palette(); var palette = new Rickshaw.Color.Palette();
if (json.Type != "matrix") { if (json.type != "matrix") {
self.showError("Result is not of matrix type! Please enter a correct expression."); self.showError("Result is not of matrix type! Please enter a correct expression.");
return []; return [];
} }
var data = json.Value.map(function(ts) { var data = json.value.map(function(ts) {
return { return {
name: escapeHTML(self.metricToTsName(ts.Metric)), name: escapeHTML(self.metricToTsName(ts.metric)),
labels: ts.Metric, labels: ts.metric,
data: ts.Values.map(function(value) { data: ts.values.map(function(value) {
return { return {
x: value.Timestamp, x: value[0],
y: self.parseValue(value.Value) y: self.parseValue(value[1])
} }
}), }),
color: palette.color() color: palette.color()
@ -506,8 +506,8 @@ Prometheus.Graph.prototype.resizeGraph = function() {
Prometheus.Graph.prototype.handleGraphResponse = function(json, textStatus) { Prometheus.Graph.prototype.handleGraphResponse = function(json, textStatus) {
var self = this var self = this
if (json.Type == "error") { if (json.type == "error") {
self.showError(json.Value); self.showError(json.value);
return; return;
} }
self.data = self.transformData(json); self.data = self.transformData(json);
@ -526,38 +526,38 @@ Prometheus.Graph.prototype.handleConsoleResponse = function(data, textStatus) {
var tBody = self.consoleTab.find(".console_table tbody"); var tBody = self.consoleTab.find(".console_table tbody");
tBody.empty(); tBody.empty();
switch(data.Type) { switch(data.type) {
case "vector": case "vector":
if (data.Value.length === 0) { if (data.value.length === 0) {
tBody.append("<tr><td colspan='2'><i>no data</i></td></tr>"); tBody.append("<tr><td colspan='2'><i>no data</i></td></tr>");
return; return;
} }
for (var i = 0; i < data.Value.length; i++) { for (var i = 0; i < data.value.length; i++) {
var v = data.Value[i]; var v = data.value[i];
var tsName = self.metricToTsName(v.Metric); var tsName = self.metricToTsName(v.metric);
tBody.append("<tr><td>" + escapeHTML(tsName) + "</td><td>" + v.Value + "</td></tr>"); tBody.append("<tr><td>" + escapeHTML(tsName) + "</td><td>" + v.value + "</td></tr>");
} }
break; break;
case "matrix": case "matrix":
if (data.Value.length === 0) { if (data.value.length === 0) {
tBody.append("<tr><td colspan='2'><i>no data</i></td></tr>"); tBody.append("<tr><td colspan='2'><i>no data</i></td></tr>");
return; return;
} }
for (var i = 0; i < data.Value.length; i++) { for (var i = 0; i < data.value.length; i++) {
var v = data.Value[i]; var v = data.value[i];
var tsName = self.metricToTsName(v.Metric); var tsName = self.metricToTsName(v.metric);
var valueText = ""; var valueText = "";
for (var j = 0; j < v.Values.length; j++) { for (var j = 0; j < v.values.length; j++) {
valueText += v.Values[j].Value + " @" + v.Values[j].Timestamp + "<br/>"; valueText += v.values[j][1] + " @" + v.values[j][0] + "<br/>";
} }
tBody.append("<tr><td>" + escapeHTML(tsName) + "</td><td>" + valueText + "</td></tr>") tBody.append("<tr><td>" + escapeHTML(tsName) + "</td><td>" + valueText + "</td></tr>")
} }
break; break;
case "scalar": case "scalar":
tBody.append("<tr><td>scalar</td><td>" + data.Value + "</td></tr>"); tBody.append("<tr><td>scalar</td><td>" + data.value + "</td></tr>");
break; break;
case "error": case "error":
self.showError(data.Value); self.showError(data.value);
break; break;
default: default:
self.showError("Unsupported value type!"); self.showError("Unsupported value type!");

View file

@ -397,11 +397,11 @@ PromConsole.Graph.prototype._render = function(data) {
// Get the data into the right format. // Get the data into the right format.
var seriesLen = 0; var seriesLen = 0;
for (var e = 0; e < data.length; e++) { for (var e = 0; e < data.length; e++) {
for (var i = 0; i < data[e].Value.length; i++) { for (var i = 0; i < data[e].value.length; i++) {
series[seriesLen++] = { series[seriesLen++] = {
data: data[e].Value[i].Values.map(function(s) {return {x: s.Timestamp, y: parseFloat(s.Value)} }), data: data[e].value[i].values.map(function(s) {return {x: s[0], y: parseFloat(s[1])} }),
color: palette.color(), color: palette.color(),
name: nameFunc(data[e].Value[i].Metric), name: nameFunc(data[e].value[i].metric),
}; };
} }
} }
@ -549,12 +549,12 @@ PromConsole._chooseNameFunction = function(data) {
// If only one label varies, use that value. // If only one label varies, use that value.
var labelValues = {}; var labelValues = {};
for (var e = 0; e < data.length; e++) { for (var e = 0; e < data.length; e++) {
for (var i = 0; i < data[e].Value.length; i++) { for (var i = 0; i < data[e].value.length; i++) {
for (var label in data[e].Value[i].Metric) { for (var label in data[e].value[i].metric) {
if (!(label in labelValues)) { if (!(label in labelValues)) {
labelValues[label] = {}; labelValues[label] = {};
} }
labelValues[label][data[e].Value[i].Metric[label]] = 1; labelValues[label][data[e].value[i].metric[label]] = 1;
} }
} }
} }