diff --git a/web/api/v1/api.go b/web/api/v1/api.go index 115416b506..dd9cc7267c 100644 --- a/web/api/v1/api.go +++ b/web/api/v1/api.go @@ -26,8 +26,9 @@ import ( "sort" "strconv" "time" + "unsafe" - "github.com/json-iterator/go" + jsoniter "github.com/json-iterator/go" "github.com/prometheus/common/model" "github.com/prometheus/common/route" "github.com/prometheus/tsdb" @@ -820,3 +821,41 @@ func parseDuration(s string) (time.Duration, error) { } return 0, fmt.Errorf("cannot parse %q to a valid duration", s) } + +func init() { + jsoniter.RegisterTypeEncoderFunc("promql.Point", marshalPointJSON, marshalPointJSONIsEmpty) +} + +func marshalPointJSON(ptr unsafe.Pointer, stream *jsoniter.Stream) { + p := *((*promql.Point)(ptr)) + stream.WriteArrayStart() + // Write out the timestamp as a float divided by 1000. + // This is ~3x faster than converting to a float. + t := p.T + if t < 0 { + stream.WriteRaw(`-`) + t = -t + } + stream.WriteInt64(t / 1000) + fraction := t % 1000 + if fraction != 0 { + stream.WriteRaw(`.`) + if fraction < 100 { + stream.WriteRaw(`0`) + } + if fraction < 10 { + stream.WriteRaw(`0`) + } + stream.WriteInt64(fraction) + } + stream.WriteMore() + stream.WriteRaw(`"`) + stream.WriteFloat64(p.V) + stream.WriteRaw(`"`) + stream.WriteArrayEnd() + +} + +func marshalPointJSONIsEmpty(ptr unsafe.Pointer) bool { + return false +} diff --git a/web/api/v1/api_test.go b/web/api/v1/api_test.go index ba6a99e878..1dc66d1adf 100644 --- a/web/api/v1/api_test.go +++ b/web/api/v1/api_test.go @@ -882,11 +882,11 @@ func TestRespond(t *testing.T) { }, { response: promql.Point{V: 20, T: 10}, - expected: `{"status":"success","data":[0.01,"20"]}`, + expected: `{"status":"success","data":[0.010,"20"]}`, }, { response: promql.Point{V: 20, T: 100}, - expected: `{"status":"success","data":[0.1,"20"]}`, + expected: `{"status":"success","data":[0.100,"20"]}`, }, { response: promql.Point{V: 20, T: 1001}, @@ -894,15 +894,15 @@ func TestRespond(t *testing.T) { }, { response: promql.Point{V: 20, T: 1010}, - expected: `{"status":"success","data":[1.01,"20"]}`, + expected: `{"status":"success","data":[1.010,"20"]}`, }, { response: promql.Point{V: 20, T: 1100}, - expected: `{"status":"success","data":[1.1,"20"]}`, + expected: `{"status":"success","data":[1.100,"20"]}`, }, { response: promql.Point{V: 20, T: 12345678123456555}, - expected: `{"status":"success","data":[12345678123456.557,"20"]}`, + expected: `{"status":"success","data":[12345678123456.555,"20"]}`, }, { response: promql.Point{V: 20, T: -1},