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

Commit 3a2251b

Browse files
authored
convert summary metrics to cumulatives and gauges. (#152)
* convert summary metrics to cumulatives and gauges. - convert summary.sum to cumulative_int64 - convert summary.count to cumulative_double - convert summary.percentile to gauge with label "percentile" * moved conversion upfront. * create separate payload for each converted metrics.
1 parent 6204395 commit 3a2251b

2 files changed

Lines changed: 301 additions & 7 deletions

File tree

metrics_proto.go

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ import (
4545

4646
var errNilMetric = errors.New("expecting a non-nil metric")
4747
var errNilMetricDescriptor = errors.New("expecting a non-nil metric descriptor")
48+
var percentileLabelKey = &metricspb.LabelKey{
49+
Key: "percentile",
50+
Description: "the value at a given percentile of a distribution",
51+
}
4852

4953
type metricProtoPayload struct {
5054
node *commonpb.Node
@@ -53,6 +57,18 @@ type metricProtoPayload struct {
5357
additionalLabels map[string]labelValue
5458
}
5559

60+
func (se *statsExporter) addPayload(node *commonpb.Node, rsc *resourcepb.Resource, labels map[string]labelValue, metrics ...*metricspb.Metric) {
61+
for _, metric := range metrics {
62+
payload := &metricProtoPayload{
63+
metric: metric,
64+
resource: rsc,
65+
node: node,
66+
additionalLabels: labels,
67+
}
68+
se.protoMetricsBundler.Add(payload, 1)
69+
}
70+
}
71+
5672
// ExportMetricsProto exports OpenCensus Metrics Proto to Stackdriver Monitoring.
5773
func (se *statsExporter) ExportMetricsProto(ctx context.Context, node *commonpb.Node, rsc *resourcepb.Resource, metrics []*metricspb.Metric) error {
5874
if len(metrics) == 0 {
@@ -66,18 +82,128 @@ func (se *statsExporter) ExportMetricsProto(ctx context.Context, node *commonpb.
6682
}
6783

6884
for _, metric := range metrics {
69-
payload := &metricProtoPayload{
70-
metric: metric,
71-
resource: rsc,
72-
node: node,
73-
additionalLabels: additionalLabels,
85+
if metric.GetMetricDescriptor().GetType() == metricspb.MetricDescriptor_SUMMARY {
86+
se.addPayload(node, rsc, additionalLabels, se.convertSummaryMetrics(metric)...)
87+
} else {
88+
se.addPayload(node, rsc, additionalLabels, metric)
7489
}
75-
se.protoMetricsBundler.Add(payload, 1)
7690
}
7791

7892
return nil
7993
}
8094

95+
func (se *statsExporter) convertSummaryMetrics(summary *metricspb.Metric) []*metricspb.Metric {
96+
var metrics []*metricspb.Metric
97+
var percentileTss []*metricspb.TimeSeries
98+
var countTss []*metricspb.TimeSeries
99+
var sumTss []*metricspb.TimeSeries
100+
101+
for _, ts := range summary.Timeseries {
102+
lvs := ts.GetLabelValues()
103+
104+
startTime := ts.StartTimestamp
105+
for _, pt := range ts.GetPoints() {
106+
ptTimestamp := pt.GetTimestamp()
107+
summaryValue := pt.GetSummaryValue()
108+
if summaryValue.Sum != nil {
109+
sumTs := &metricspb.TimeSeries{
110+
LabelValues: lvs,
111+
StartTimestamp: startTime,
112+
Points: []*metricspb.Point{
113+
{
114+
Value: &metricspb.Point_DoubleValue{
115+
DoubleValue: summaryValue.Sum.Value,
116+
},
117+
Timestamp: ptTimestamp,
118+
},
119+
},
120+
}
121+
sumTss = append(sumTss, sumTs)
122+
}
123+
124+
if summaryValue.Count != nil {
125+
countTs := &metricspb.TimeSeries{
126+
LabelValues: lvs,
127+
StartTimestamp: startTime,
128+
Points: []*metricspb.Point{
129+
{
130+
Value: &metricspb.Point_Int64Value{
131+
Int64Value: summaryValue.Count.Value,
132+
},
133+
Timestamp: ptTimestamp,
134+
},
135+
},
136+
}
137+
countTss = append(countTss, countTs)
138+
}
139+
140+
snapshot := summaryValue.GetSnapshot()
141+
for _, percentileValue := range snapshot.GetPercentileValues() {
142+
lvsWithPercentile := lvs[0:]
143+
lvsWithPercentile = append(lvsWithPercentile, &metricspb.LabelValue{
144+
Value: fmt.Sprintf("%f", percentileValue.Percentile),
145+
})
146+
percentileTs := &metricspb.TimeSeries{
147+
LabelValues: lvsWithPercentile,
148+
StartTimestamp: nil,
149+
Points: []*metricspb.Point{
150+
{
151+
Value: &metricspb.Point_DoubleValue{
152+
DoubleValue: percentileValue.Value,
153+
},
154+
Timestamp: ptTimestamp,
155+
},
156+
},
157+
}
158+
percentileTss = append(percentileTss, percentileTs)
159+
}
160+
}
161+
162+
if len(sumTss) > 0 {
163+
metric := &metricspb.Metric{
164+
MetricDescriptor: &metricspb.MetricDescriptor{
165+
Name: fmt.Sprintf("%s_summary_sum", summary.GetMetricDescriptor().GetName()),
166+
Description: summary.GetMetricDescriptor().GetDescription(),
167+
Type: metricspb.MetricDescriptor_CUMULATIVE_DOUBLE,
168+
Unit: summary.GetMetricDescriptor().GetUnit(),
169+
LabelKeys: summary.GetMetricDescriptor().GetLabelKeys(),
170+
},
171+
Timeseries: sumTss,
172+
}
173+
metrics = append(metrics, metric)
174+
}
175+
if len(countTss) > 0 {
176+
metric := &metricspb.Metric{
177+
MetricDescriptor: &metricspb.MetricDescriptor{
178+
Name: fmt.Sprintf("%s_summary_count", summary.GetMetricDescriptor().GetName()),
179+
Description: summary.GetMetricDescriptor().GetDescription(),
180+
Type: metricspb.MetricDescriptor_CUMULATIVE_INT64,
181+
Unit: "1",
182+
LabelKeys: summary.GetMetricDescriptor().GetLabelKeys(),
183+
},
184+
Timeseries: countTss,
185+
}
186+
metrics = append(metrics, metric)
187+
}
188+
if len(percentileTss) > 0 {
189+
lks := summary.GetMetricDescriptor().GetLabelKeys()[0:]
190+
lks = append(lks, percentileLabelKey)
191+
metric := &metricspb.Metric{
192+
MetricDescriptor: &metricspb.MetricDescriptor{
193+
Name: fmt.Sprintf("%s_summary_percentile", summary.GetMetricDescriptor().GetName()),
194+
Description: summary.GetMetricDescriptor().GetDescription(),
195+
Type: metricspb.MetricDescriptor_GAUGE_DOUBLE,
196+
Unit: summary.GetMetricDescriptor().GetUnit(),
197+
LabelKeys: lks,
198+
},
199+
Timeseries: percentileTss,
200+
}
201+
metrics = append(metrics, metric)
202+
}
203+
}
204+
return metrics
205+
}
206+
81207
func (se *statsExporter) handleMetricsProtoUpload(payloads []*metricProtoPayload) error {
82208
ctx, cancel := se.o.newContextWithTimeout()
83209
defer cancel()

metrics_proto_test.go

Lines changed: 169 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"strings"
2121
"testing"
2222

23-
monitoring "cloud.google.com/go/monitoring/apiv3"
23+
"cloud.google.com/go/monitoring/apiv3"
2424
"github.com/golang/protobuf/ptypes/timestamp"
2525
distributionpb "google.golang.org/genproto/googleapis/api/distribution"
2626
labelpb "google.golang.org/genproto/googleapis/api/label"
@@ -31,6 +31,8 @@ import (
3131
commonpb "github.com/census-instrumentation/opencensus-proto/gen-go/agent/common/v1"
3232
metricspb "github.com/census-instrumentation/opencensus-proto/gen-go/metrics/v1"
3333
resourcepb "github.com/census-instrumentation/opencensus-proto/gen-go/resource/v1"
34+
"github.com/golang/protobuf/ptypes/wrappers"
35+
"github.com/google/go-cmp/cmp"
3436
)
3537

3638
func TestProtoResourceToMonitoringResource(t *testing.T) {
@@ -515,3 +517,169 @@ func TestNodeToDefaultLabels(t *testing.T) {
515517
}
516518
}
517519
}
520+
521+
func TestConvertSummaryMetrics(t *testing.T) {
522+
startTimestamp := &timestamp.Timestamp{
523+
Seconds: 1543160298,
524+
Nanos: 100000090,
525+
}
526+
endTimestamp := &timestamp.Timestamp{
527+
Seconds: 1543160298,
528+
Nanos: 100000997,
529+
}
530+
531+
tests := []struct {
532+
in *metricspb.Metric
533+
want []*metricspb.Metric
534+
statsExporter *statsExporter
535+
}{
536+
{
537+
in: &metricspb.Metric{
538+
MetricDescriptor: &metricspb.MetricDescriptor{
539+
Name: "summary_metric_descriptor",
540+
Description: "This is a test",
541+
Unit: "ms",
542+
Type: metricspb.MetricDescriptor_SUMMARY,
543+
},
544+
Timeseries: []*metricspb.TimeSeries{
545+
{
546+
StartTimestamp: startTimestamp,
547+
Points: []*metricspb.Point{
548+
{
549+
Timestamp: endTimestamp,
550+
Value: &metricspb.Point_SummaryValue{
551+
SummaryValue: &metricspb.SummaryValue{
552+
Count: &wrappers.Int64Value{Value: 10},
553+
Sum: &wrappers.DoubleValue{Value: 119.0},
554+
Snapshot: &metricspb.SummaryValue_Snapshot{
555+
PercentileValues: []*metricspb.SummaryValue_Snapshot_ValueAtPercentile{
556+
makePercentileValue(5.6, 10.0),
557+
makePercentileValue(9.6, 50.0),
558+
makePercentileValue(12.6, 90.0),
559+
makePercentileValue(19.6, 99.0),
560+
},
561+
},
562+
},
563+
},
564+
},
565+
},
566+
},
567+
},
568+
},
569+
statsExporter: &statsExporter{
570+
o: Options{ProjectID: "foo"},
571+
},
572+
want: []*metricspb.Metric{
573+
{
574+
MetricDescriptor: &metricspb.MetricDescriptor{
575+
Name: "summary_metric_descriptor_summary_sum",
576+
Description: "This is a test",
577+
Unit: "ms",
578+
Type: metricspb.MetricDescriptor_CUMULATIVE_DOUBLE,
579+
},
580+
Timeseries: []*metricspb.TimeSeries{
581+
makeDoubleTs(119.0, "", startTimestamp, endTimestamp),
582+
},
583+
},
584+
{
585+
MetricDescriptor: &metricspb.MetricDescriptor{
586+
Name: "summary_metric_descriptor_summary_count",
587+
Description: "This is a test",
588+
Unit: "1",
589+
Type: metricspb.MetricDescriptor_CUMULATIVE_INT64,
590+
},
591+
Timeseries: []*metricspb.TimeSeries{
592+
makeInt64Ts(10, "", startTimestamp, endTimestamp),
593+
},
594+
},
595+
{
596+
MetricDescriptor: &metricspb.MetricDescriptor{
597+
Name: "summary_metric_descriptor_summary_percentile",
598+
Description: "This is a test",
599+
Unit: "ms",
600+
Type: metricspb.MetricDescriptor_GAUGE_DOUBLE,
601+
LabelKeys: []*metricspb.LabelKey{
602+
percentileLabelKey,
603+
},
604+
},
605+
Timeseries: []*metricspb.TimeSeries{
606+
makeDoubleTs(5.6, "10.000000", nil, endTimestamp),
607+
makeDoubleTs(9.6, "50.000000", nil, endTimestamp),
608+
makeDoubleTs(12.6, "90.000000", nil, endTimestamp),
609+
makeDoubleTs(19.6, "99.000000", nil, endTimestamp),
610+
},
611+
},
612+
},
613+
},
614+
}
615+
616+
for _, tt := range tests {
617+
se := tt.statsExporter
618+
if se == nil {
619+
se = new(statsExporter)
620+
}
621+
got := se.convertSummaryMetrics(tt.in)
622+
if !cmp.Equal(got, tt.want) {
623+
t.Fatalf("conversion failed:\n got=%v\n want=%v\n", got, tt.want)
624+
}
625+
}
626+
}
627+
628+
func makeInt64Ts(val int64, label string, start, end *timestamp.Timestamp) *metricspb.TimeSeries {
629+
ts := &metricspb.TimeSeries{
630+
StartTimestamp: start,
631+
Points: makeInt64Point(val, end),
632+
}
633+
if label != "" {
634+
ts.LabelValues = makeLabelValue(label)
635+
}
636+
return ts
637+
}
638+
639+
func makeInt64Point(val int64, end *timestamp.Timestamp) []*metricspb.Point {
640+
return []*metricspb.Point{
641+
{
642+
Timestamp: end,
643+
Value: &metricspb.Point_Int64Value{
644+
Int64Value: val,
645+
},
646+
},
647+
}
648+
}
649+
650+
func makeDoubleTs(val float64, label string, start, end *timestamp.Timestamp) *metricspb.TimeSeries {
651+
ts := &metricspb.TimeSeries{
652+
StartTimestamp: start,
653+
Points: makeDoublePoint(val, end),
654+
}
655+
if label != "" {
656+
ts.LabelValues = makeLabelValue(label)
657+
}
658+
return ts
659+
}
660+
661+
func makeDoublePoint(val float64, end *timestamp.Timestamp) []*metricspb.Point {
662+
return []*metricspb.Point{
663+
{
664+
Timestamp: end,
665+
Value: &metricspb.Point_DoubleValue{
666+
DoubleValue: val,
667+
},
668+
},
669+
}
670+
}
671+
672+
func makeLabelValue(value string) []*metricspb.LabelValue {
673+
return []*metricspb.LabelValue{
674+
{
675+
Value: value,
676+
},
677+
}
678+
}
679+
680+
func makePercentileValue(val, percentile float64) *metricspb.SummaryValue_Snapshot_ValueAtPercentile {
681+
return &metricspb.SummaryValue_Snapshot_ValueAtPercentile{
682+
Value: val,
683+
Percentile: percentile,
684+
}
685+
}

0 commit comments

Comments
 (0)