Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit c719bc1

Browse files
committed
OpenCensusProto Point to Stackdriver Point
An adapter to directly convert from OpenCensus Proto Point to Stackdriver's monitoring/v3 Point which are both protobuf based code backed representations. This the first of a series of changes to allow direct conversion of metrics so that we can use this exporter in the OpenCensus Agent/Service exporters, but also so that applications such as OpenCensus-PHP's daemon can begin to it too. Also added a TODO for SummaryValue conversion: A reminder that we should support the conversion from metricspb.SummaryValue to monitoring/v3 proto whenever #66 is solved. Updates #64.
1 parent 899e456 commit c719bc1

2 files changed

Lines changed: 281 additions & 0 deletions

File tree

metrics.go

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package stackdriver
16+
17+
/*
18+
The code in this file is responsible for converting OpenCensus Proto metrics
19+
directly to Stackdriver Metrics.
20+
*/
21+
22+
import (
23+
"fmt"
24+
25+
"github.com/golang/protobuf/ptypes/timestamp"
26+
27+
distributionpb "google.golang.org/genproto/googleapis/api/distribution"
28+
monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3"
29+
30+
metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
31+
)
32+
33+
func fromProtoPoint(startTime *timestamp.Timestamp, pt *metricspb.Point) (*monitoringpb.Point, error) {
34+
if pt == nil {
35+
return nil, nil
36+
}
37+
38+
mptv, err := protoToMetricPoint(pt.Value)
39+
if err != nil {
40+
return nil, err
41+
}
42+
43+
mpt := &monitoringpb.Point{
44+
Value: mptv,
45+
Interval: &monitoringpb.TimeInterval{
46+
StartTime: startTime,
47+
EndTime: pt.Timestamp,
48+
},
49+
}
50+
return mpt, nil
51+
}
52+
53+
func protoToMetricPoint(value interface{}) (*monitoringpb.TypedValue, error) {
54+
if value == nil {
55+
return nil, nil
56+
}
57+
58+
var err error
59+
var tval *monitoringpb.TypedValue
60+
switch v := value.(type) {
61+
default:
62+
// All the other types are not yet handled.
63+
// TODO: (@odeke-em, @songy23) talk to the Stackdriver team to determine
64+
// the use cases for:
65+
//
66+
// *TypedValue_BoolValue
67+
// *TypedValue_StringValue
68+
//
69+
// and then file feature requests on OpenCensus-Specs and then OpenCensus-Proto,
70+
// lest we shall error here.
71+
//
72+
// TODO: Add conversion from SummaryValue when
73+
// https://github.com/census-ecosystem/opencensus-go-exporter-stackdriver/issues/66
74+
// has been figured out.
75+
err = fmt.Errorf("protoToMetricPoint: unknown Data type: %T", value)
76+
77+
case *metricspb.Point_Int64Value:
78+
tval = &monitoringpb.TypedValue{
79+
Value: &monitoringpb.TypedValue_Int64Value{
80+
Int64Value: v.Int64Value,
81+
},
82+
}
83+
84+
case *metricspb.Point_DoubleValue:
85+
tval = &monitoringpb.TypedValue{
86+
Value: &monitoringpb.TypedValue_DoubleValue{
87+
DoubleValue: v.DoubleValue,
88+
},
89+
}
90+
91+
case *metricspb.Point_DistributionValue:
92+
dv := v.DistributionValue
93+
var mv *monitoringpb.TypedValue_DistributionValue
94+
if dv != nil {
95+
var mean float64
96+
if dv.Count > 0 {
97+
mean = float64(dv.Sum) / float64(dv.Count)
98+
}
99+
mv = &monitoringpb.TypedValue_DistributionValue{
100+
DistributionValue: &distributionpb.Distribution{
101+
Count: dv.Count,
102+
Mean: mean,
103+
SumOfSquaredDeviation: dv.SumOfSquaredDeviation,
104+
BucketCounts: bucketCounts(dv.Buckets),
105+
},
106+
}
107+
108+
if bopts := dv.BucketOptions; bopts != nil && bopts.Type != nil {
109+
bexp, ok := bopts.Type.(*metricspb.DistributionValue_BucketOptions_Explicit_)
110+
if ok && bexp != nil && bexp.Explicit != nil {
111+
mv.DistributionValue.BucketOptions = &distributionpb.Distribution_BucketOptions{
112+
Options: &distributionpb.Distribution_BucketOptions_ExplicitBuckets{
113+
ExplicitBuckets: &distributionpb.Distribution_BucketOptions_Explicit{
114+
Bounds: bexp.Explicit.Bounds[:],
115+
},
116+
},
117+
}
118+
}
119+
}
120+
}
121+
tval = &monitoringpb.TypedValue{Value: mv}
122+
}
123+
124+
return tval, err
125+
}
126+
127+
func bucketCounts(buckets []*metricspb.DistributionValue_Bucket) []int64 {
128+
bucketCounts := make([]int64, len(buckets))
129+
for i, bucket := range buckets {
130+
if bucket != nil {
131+
bucketCounts[i] = bucket.Count
132+
}
133+
}
134+
return bucketCounts
135+
}

metrics_test.go

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Copyright 2018, OpenCensus Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package stackdriver
16+
17+
import (
18+
"encoding/json"
19+
"reflect"
20+
"testing"
21+
22+
"github.com/golang/protobuf/ptypes/timestamp"
23+
distributionpb "google.golang.org/genproto/googleapis/api/distribution"
24+
monitoringpb "google.golang.org/genproto/googleapis/monitoring/v3"
25+
26+
metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
27+
)
28+
29+
func TestProtoMetricsToMonitoringMetrics_fromProtoPoint(t *testing.T) {
30+
startTimestamp := &timestamp.Timestamp{
31+
Seconds: 1543160298,
32+
Nanos: 100000090,
33+
}
34+
endTimestamp := &timestamp.Timestamp{
35+
Seconds: 1543160298,
36+
Nanos: 100000997,
37+
}
38+
39+
tests := []struct {
40+
in *metricspb.Point
41+
want *monitoringpb.Point
42+
wantErr string
43+
}{
44+
{
45+
in: &metricspb.Point{
46+
Timestamp: endTimestamp,
47+
Value: &metricspb.Point_DistributionValue{
48+
DistributionValue: &metricspb.DistributionValue{
49+
Count: 1,
50+
Sum: 11.9,
51+
SumOfSquaredDeviation: 0,
52+
Buckets: []*metricspb.DistributionValue_Bucket{
53+
{}, {Count: 1}, {}, {}, {},
54+
},
55+
BucketOptions: &metricspb.DistributionValue_BucketOptions{
56+
Type: &metricspb.DistributionValue_BucketOptions_Explicit_{
57+
Explicit: &metricspb.DistributionValue_BucketOptions_Explicit{
58+
Bounds: []float64{0, 10, 20, 30, 40},
59+
},
60+
},
61+
},
62+
},
63+
},
64+
},
65+
want: &monitoringpb.Point{
66+
Interval: &monitoringpb.TimeInterval{
67+
StartTime: startTimestamp,
68+
EndTime: endTimestamp,
69+
},
70+
Value: &monitoringpb.TypedValue{
71+
Value: &monitoringpb.TypedValue_DistributionValue{
72+
DistributionValue: &distributionpb.Distribution{
73+
Count: 1,
74+
Mean: 11.9,
75+
SumOfSquaredDeviation: 0,
76+
BucketCounts: []int64{0, 1, 0, 0, 0},
77+
BucketOptions: &distributionpb.Distribution_BucketOptions{
78+
Options: &distributionpb.Distribution_BucketOptions_ExplicitBuckets{
79+
ExplicitBuckets: &distributionpb.Distribution_BucketOptions_Explicit{
80+
Bounds: []float64{0, 10, 20, 30, 40},
81+
},
82+
},
83+
},
84+
},
85+
},
86+
},
87+
},
88+
},
89+
{
90+
in: &metricspb.Point{
91+
Timestamp: endTimestamp,
92+
Value: &metricspb.Point_DoubleValue{DoubleValue: 50},
93+
},
94+
want: &monitoringpb.Point{
95+
Interval: &monitoringpb.TimeInterval{
96+
StartTime: startTimestamp,
97+
EndTime: endTimestamp,
98+
},
99+
Value: &monitoringpb.TypedValue{
100+
Value: &monitoringpb.TypedValue_DoubleValue{DoubleValue: 50},
101+
},
102+
},
103+
},
104+
{
105+
in: &metricspb.Point{
106+
Timestamp: endTimestamp,
107+
Value: &metricspb.Point_Int64Value{Int64Value: 17},
108+
},
109+
want: &monitoringpb.Point{
110+
Interval: &monitoringpb.TimeInterval{
111+
StartTime: startTimestamp,
112+
EndTime: endTimestamp,
113+
},
114+
Value: &monitoringpb.TypedValue{
115+
Value: &monitoringpb.TypedValue_Int64Value{Int64Value: 17},
116+
},
117+
},
118+
},
119+
}
120+
121+
for i, tt := range tests {
122+
mpt, err := fromProtoPoint(startTimestamp, tt.in)
123+
if tt.wantErr != "" {
124+
continue
125+
}
126+
127+
if err != nil {
128+
t.Errorf("#%d: unexpected error: %v", i, err)
129+
continue
130+
}
131+
132+
if g, w := mpt, tt.want; !reflect.DeepEqual(g, w) {
133+
// Our saving grace is serialization equality since some
134+
// unexported fields could be present in the various values.
135+
gj, wj := serializeAsJSON(g), serializeAsJSON(w)
136+
if gj != wj {
137+
t.Errorf("#%d: Unmatched JSON\nGot:\n\t%s\nWant:\n\t%s", i, gj, wj)
138+
}
139+
}
140+
}
141+
}
142+
143+
func serializeAsJSON(v interface{}) string {
144+
blob, _ := json.MarshalIndent(v, "", " ")
145+
return string(blob)
146+
}

0 commit comments

Comments
 (0)