// Copyright 2016 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package v1 import ( "unsafe" jsoniter "github.com/json-iterator/go" "github.com/prometheus/prometheus/model/exemplar" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/util/jsonutil" ) func init() { jsoniter.RegisterTypeEncoderFunc("promql.Vector", unsafeMarshalVectorJSON, neverEmpty) jsoniter.RegisterTypeEncoderFunc("promql.Matrix", unsafeMarshalMatrixJSON, neverEmpty) jsoniter.RegisterTypeEncoderFunc("promql.Series", unsafeMarshalSeriesJSON, neverEmpty) jsoniter.RegisterTypeEncoderFunc("promql.Sample", unsafeMarshalSampleJSON, neverEmpty) jsoniter.RegisterTypeEncoderFunc("promql.FPoint", unsafeMarshalFPointJSON, neverEmpty) jsoniter.RegisterTypeEncoderFunc("promql.HPoint", unsafeMarshalHPointJSON, neverEmpty) jsoniter.RegisterTypeEncoderFunc("exemplar.Exemplar", marshalExemplarJSON, neverEmpty) jsoniter.RegisterTypeEncoderFunc("labels.Labels", unsafeMarshalLabelsJSON, labelsIsEmpty) } // JSONCodec is a Codec that encodes API responses as JSON. type JSONCodec struct{} func (j JSONCodec) ContentType() MIMEType { return MIMEType{Type: "application", SubType: "json"} } func (j JSONCodec) CanEncode(_ *Response) bool { return true } func (j JSONCodec) Encode(resp *Response) ([]byte, error) { json := jsoniter.ConfigCompatibleWithStandardLibrary return json.Marshal(resp) } // marshalSeriesJSON writes something like the following: // // { // "metric" : { // "__name__" : "up", // "job" : "prometheus", // "instance" : "localhost:9090" // }, // "values": [ // [ 1435781451.781, "1" ], // < more values> // ], // "histograms": [ // [ 1435781451.781, { < histogram, see jsonutil.MarshalHistogram > } ], // < more histograms > // ], // }, func unsafeMarshalSeriesJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { s := *((*promql.Series)(ptr)) marshalSeriesJSON(s, stream) } func marshalSeriesJSON(s promql.Series, stream *jsoniter.Stream) { stream.WriteObjectStart() stream.WriteObjectField(`metric`) marshalLabelsJSON(s.Metric, stream) for i, p := range s.Floats { stream.WriteMore() if i == 0 { stream.WriteObjectField(`values`) stream.WriteArrayStart() } marshalFPointJSON(p, stream) } if len(s.Floats) > 0 { stream.WriteArrayEnd() } for i, p := range s.Histograms { stream.WriteMore() if i == 0 { stream.WriteObjectField(`histograms`) stream.WriteArrayStart() } marshalHPointJSON(p, stream) } if len(s.Histograms) > 0 { stream.WriteArrayEnd() } stream.WriteObjectEnd() } // In the Prometheus API we render an empty object as `[]` or similar. func neverEmpty(unsafe.Pointer) bool { return false } // marshalSampleJSON writes something like the following for normal value samples: // // { // "metric" : { // "__name__" : "up", // "job" : "prometheus", // "instance" : "localhost:9090" // }, // "value": [ 1435781451.781, "1.234" ] // }, // // For histogram samples, it writes something like this: // // { // "metric" : { // "__name__" : "up", // "job" : "prometheus", // "instance" : "localhost:9090" // }, // "histogram": [ 1435781451.781, { < histogram, see jsonutil.MarshalHistogram > } ] // }, func unsafeMarshalSampleJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { s := *((*promql.Sample)(ptr)) marshalSampleJSON(s, stream) } func marshalSampleJSON(s promql.Sample, stream *jsoniter.Stream) { stream.WriteObjectStart() stream.WriteObjectField(`metric`) marshalLabelsJSON(s.Metric, stream) stream.WriteMore() if s.H == nil { stream.WriteObjectField(`value`) } else { stream.WriteObjectField(`histogram`) } stream.WriteArrayStart() jsonutil.MarshalTimestamp(s.T, stream) stream.WriteMore() if s.H == nil { jsonutil.MarshalFloat(s.F, stream) } else { jsonutil.MarshalHistogram(s.H, stream) } stream.WriteArrayEnd() stream.WriteObjectEnd() } // marshalFPointJSON writes `[ts, "1.234"]`. func unsafeMarshalFPointJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { p := *((*promql.FPoint)(ptr)) marshalFPointJSON(p, stream) } func marshalFPointJSON(p promql.FPoint, stream *jsoniter.Stream) { stream.WriteArrayStart() jsonutil.MarshalTimestamp(p.T, stream) stream.WriteMore() jsonutil.MarshalFloat(p.F, stream) stream.WriteArrayEnd() } // marshalHPointJSON writes `[ts, { < histogram, see jsonutil.MarshalHistogram > } ]`. func unsafeMarshalHPointJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { p := *((*promql.HPoint)(ptr)) marshalHPointJSON(p, stream) } func marshalHPointJSON(p promql.HPoint, stream *jsoniter.Stream) { stream.WriteArrayStart() jsonutil.MarshalTimestamp(p.T, stream) stream.WriteMore() jsonutil.MarshalHistogram(p.H, stream) stream.WriteArrayEnd() } // marshalExemplarJSON writes. // // { // labels: , // value: "", // timestamp: // } func marshalExemplarJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { p := *((*exemplar.Exemplar)(ptr)) stream.WriteObjectStart() // "labels" key. stream.WriteObjectField(`labels`) marshalLabelsJSON(p.Labels, stream) // "value" key. stream.WriteMore() stream.WriteObjectField(`value`) jsonutil.MarshalFloat(p.Value, stream) // "timestamp" key. stream.WriteMore() stream.WriteObjectField(`timestamp`) jsonutil.MarshalTimestamp(p.Ts, stream) stream.WriteObjectEnd() } func unsafeMarshalLabelsJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { labelsPtr := (*labels.Labels)(ptr) marshalLabelsJSON(*labelsPtr, stream) } func marshalLabelsJSON(lbls labels.Labels, stream *jsoniter.Stream) { stream.WriteObjectStart() i := 0 lbls.Range(func(v labels.Label) { if i != 0 { stream.WriteMore() } i++ stream.WriteString(v.Name) stream.WriteRaw(`:`) stream.WriteString(v.Value) }) stream.WriteObjectEnd() } func labelsIsEmpty(ptr unsafe.Pointer) bool { labelsPtr := (*labels.Labels)(ptr) return labelsPtr.IsEmpty() } // Marshal a Vector as `[sample,sample,...]` - empty Vector is `[]`. func unsafeMarshalVectorJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { v := *((*promql.Vector)(ptr)) stream.WriteArrayStart() for i, s := range v { marshalSampleJSON(s, stream) if i != len(v)-1 { stream.WriteMore() } } stream.WriteArrayEnd() } // Marshal a Matrix as `[series,series,...]` - empty Matrix is `[]`. func unsafeMarshalMatrixJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { m := *((*promql.Matrix)(ptr)) stream.WriteArrayStart() for i, s := range m { marshalSeriesJSON(s, stream) if i != len(m)-1 { stream.WriteMore() } } stream.WriteArrayEnd() }