internal/telemetry/export/ocagent: correctly marshal points to JSON

This change adds a custom MarshalJSON func to Point so that values
are formatted the same way jsonpb formats them. This allows the
OpenCensus service to determine the concrete type of the point's
value when unmarshaling.

What works:
* Points with Int64, Double, and Distribution values will marshal correctly.

What does not work:
* Points with Summary values will not marshal.

Updates golang/go#33819

Change-Id: Ia76ebff4e0e2b6ff2ddf72b8d6187f49069d4cad
Reviewed-on: https://go-review.googlesource.com/c/tools/+/207838
Reviewed-by: Emmanuel Odeke <emm.odeke@gmail.com>
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Nathan Dias 2019-11-19 01:37:28 -06:00 committed by Emmanuel Odeke
parent 82924fac8e
commit 96ad48e4b0
2 changed files with 123 additions and 0 deletions

View File

@ -4,6 +4,11 @@
package wire
import (
"encoding/json"
"fmt"
)
type ExportMetricsServiceRequest struct {
Node *Node `json:"node,omitempty"`
Metrics []*Metric `json:"metrics,omitempty"`
@ -62,6 +67,48 @@ type PointInt64Value struct {
Int64Value int64 `json:"int64Value,omitempty"`
}
// MarshalJSON creates JSON formatted the same way as jsonpb so that the
// OpenCensus service can correctly determine the underlying value type.
// This custom MarshalJSON exists because,
// by default *Point is JSON marshalled as:
// {"value": {"int64Value": 1}}
// but it should be marshalled as:
// {"int64Value": 1}
func (p *Point) MarshalJSON() ([]byte, error) {
if p == nil {
return []byte("null"), nil
}
switch d := p.Value.(type) {
case PointInt64Value:
return json.Marshal(&struct {
Timestamp *Timestamp `json:"timestamp,omitempty"`
Value int64 `json:"int64Value,omitempty"`
}{
Timestamp: p.Timestamp,
Value: d.Int64Value,
})
case PointDoubleValue:
return json.Marshal(&struct {
Timestamp *Timestamp `json:"timestamp,omitempty"`
Value float64 `json:"doubleValue,omitempty"`
}{
Timestamp: p.Timestamp,
Value: d.DoubleValue,
})
case PointDistributionValue:
return json.Marshal(&struct {
Timestamp *Timestamp `json:"timestamp,omitempty"`
Value *DistributionValue `json:"distributionValue,omitempty"`
}{
Timestamp: p.Timestamp,
Value: d.DistributionValue,
})
default:
return nil, fmt.Errorf("unknown point type %T", p.Value)
}
}
type PointDoubleValue struct {
DoubleValue float64 `json:"doubleValue,omitempty"`
}

View File

@ -0,0 +1,76 @@
package wire
import (
"reflect"
"testing"
)
func TestMarshalJSON(t *testing.T) {
tests := []struct {
name string
pt *Point
want string
}{
{
"PointInt64",
&Point{
Value: PointInt64Value{
Int64Value: 5,
},
},
`{"int64Value":5}`,
},
{
"PointDouble",
&Point{
Value: PointDoubleValue{
DoubleValue: 3.14,
},
},
`{"doubleValue":3.14}`,
},
{
"PointDistribution",
&Point{
Value: PointDistributionValue{
DistributionValue: &DistributionValue{
Count: 3,
Sum: 10,
Buckets: []*Bucket{
{
Count: 1,
},
{
Count: 2,
},
},
BucketOptions: BucketOptionsExplicit{
Bounds: []float64{
0, 5,
},
},
},
},
},
`{"distributionValue":{"count":3,"sum":10,"bucket_options":{"bounds":[0,5]},"buckets":[{"count":1},{"count":2}]}}`,
},
{
"nil point",
nil,
`null`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
buf, err := tt.pt.MarshalJSON()
if err != nil {
t.Fatalf("Got:\n%v\nWant:\n%v", err, nil)
}
got := string(buf)
if !reflect.DeepEqual(got, tt.want) {
t.Fatalf("Got:\n%s\nWant:\n%s", got, tt.want)
}
})
}
}