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.
summary: "Summary",
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.
type SampleStream struct {
Metric clientmodel.COWMetric
Values metric.Values
Metric clientmodel.COWMetric `json:"metric"`
Values metric.Values `json:"values"`
}
// Sample is a single sample belonging to a COWMetric.
type Sample struct {
Metric clientmodel.COWMetric
Value clientmodel.SampleValue
Timestamp clientmodel.Timestamp
Metric clientmodel.COWMetric `json:"metric"`
Value clientmodel.SampleValue `json:"value"`
Timestamp clientmodel.Timestamp `json:"timestamp"`
}
// Vector is basically only an alias for clientmodel.Samples, but the

View file

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

View file

@ -15,13 +15,14 @@ package metric
import (
"fmt"
"strconv"
clientmodel "github.com/prometheus/client_golang/model"
)
// MarshalJSON implements json.Marshaler.
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.

View file

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

View file

@ -397,11 +397,11 @@ PromConsole.Graph.prototype._render = function(data) {
// Get the data into the right format.
var seriesLen = 0;
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++] = {
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(),
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.
var labelValues = {};
for (var e = 0; e < data.length; e++) {
for (var i = 0; i < data[e].Value.length; i++) {
for (var label in data[e].Value[i].Metric) {
for (var i = 0; i < data[e].value.length; i++) {
for (var label in data[e].value[i].metric) {
if (!(label in labelValues)) {
labelValues[label] = {};
}
labelValues[label][data[e].Value[i].Metric[label]] = 1;
labelValues[label][data[e].value[i].metric[label]] = 1;
}
}
}