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

Commit 524cf4c

Browse files
authored
Exporter/Stats/Stackdriver: handle Summary Type (#1591)
* Add Summary value in SD * move summaryMetricToDoubleGaugeMetric to StackdriverExportUtils * fix review comments * Gauge TimeSeries: Add null for startTimestamp * Use default unit: 1 for count metric
1 parent f1ffd06 commit 524cf4c

File tree

3 files changed

+308
-5
lines changed

3 files changed

+308
-5
lines changed

exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtils.java

Lines changed: 133 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
import io.opencensus.metrics.export.Distribution.BucketOptions.ExplicitOptions;
4545
import io.opencensus.metrics.export.MetricDescriptor.Type;
4646
import io.opencensus.metrics.export.Summary;
47+
import io.opencensus.metrics.export.Summary.Snapshot;
48+
import io.opencensus.metrics.export.Summary.Snapshot.ValueAtPercentile;
4749
import io.opencensus.metrics.export.Value;
4850
import io.opencensus.resource.Resource;
4951
import java.lang.management.ManagementFactory;
@@ -83,6 +85,16 @@ final class StackdriverExportUtils {
8385
private static final Map<String, String> K8S_RESOURCE_MAPPING = getK8sResourceLabelsMappings();
8486
private static final Map<String, String> AWS_RESOURCE_MAPPING = getAwsResourceLabelsMappings();
8587

88+
@VisibleForTesting
89+
static final LabelKey PERCENTILE_LABEL_KEY =
90+
LabelKey.create("percentile", "the value at a given percentile of a distribution");
91+
92+
@VisibleForTesting
93+
static final String SNAPSHOT_SUFFIX_PERCENTILE = "_summary_snapshot_percentile";
94+
95+
@VisibleForTesting static final String SUMMARY_SUFFIX_COUNT = "_summary_count";
96+
@VisibleForTesting static final String SUMMARY_SUFFIX_SUM = "_summary_sum";
97+
8698
// Constant functions for TypedValue.
8799
private static final Function<Double, TypedValue> typedValueDoubleFunction =
88100
new Function<Double, TypedValue>() {
@@ -115,8 +127,6 @@ public TypedValue apply(io.opencensus.metrics.export.Distribution arg) {
115127
new Function<Summary, TypedValue>() {
116128
@Override
117129
public TypedValue apply(Summary arg) {
118-
// StackDriver doesn't handle Summary value.
119-
// TODO(mayurkale): decide what to do with Summary value.
120130
TypedValue.Builder builder = TypedValue.newBuilder();
121131
return builder.build();
122132
}
@@ -222,7 +232,6 @@ static MetricKind createMetricKind(Type type) {
222232
// Convert a OpenCensus Type to a StackDriver ValueType
223233
@VisibleForTesting
224234
static MetricDescriptor.ValueType createValueType(Type type) {
225-
// TODO(mayurkale): decide what to do with Summary type.
226235
if (type == Type.CUMULATIVE_DOUBLE || type == Type.GAUGE_DOUBLE) {
227236
return MetricDescriptor.ValueType.DOUBLE;
228237
} else if (type == Type.GAUGE_INT64 || type == Type.CUMULATIVE_INT64) {
@@ -425,6 +434,127 @@ static void setResourceForBuilder(
425434
throw new IllegalArgumentException("Unknown subclass of MonitoredResource.");
426435
}
427436

437+
@VisibleForTesting
438+
static List<io.opencensus.metrics.export.Metric> convertSummaryMetric(
439+
io.opencensus.metrics.export.Metric summaryMetric) {
440+
List<io.opencensus.metrics.export.Metric> metricsList = Lists.newArrayList();
441+
final List<io.opencensus.metrics.export.TimeSeries> percentileTimeSeries = new ArrayList<>();
442+
final List<io.opencensus.metrics.export.TimeSeries> summaryCountTimeSeries = new ArrayList<>();
443+
final List<io.opencensus.metrics.export.TimeSeries> summarySumTimeSeries = new ArrayList<>();
444+
for (final io.opencensus.metrics.export.TimeSeries timeSeries :
445+
summaryMetric.getTimeSeriesList()) {
446+
final List<LabelValue> labelValuesWithPercentile =
447+
new ArrayList<>(timeSeries.getLabelValues());
448+
final io.opencensus.common.Timestamp timeSeriesTimestamp = timeSeries.getStartTimestamp();
449+
for (io.opencensus.metrics.export.Point point : timeSeries.getPoints()) {
450+
final io.opencensus.common.Timestamp pointTimestamp = point.getTimestamp();
451+
point
452+
.getValue()
453+
.match(
454+
Functions.<Void>returnNull(),
455+
Functions.<Void>returnNull(),
456+
Functions.<Void>returnNull(),
457+
new Function<Summary, Void>() {
458+
@Override
459+
public Void apply(Summary summary) {
460+
Long count = summary.getCount();
461+
if (count != null) {
462+
createTimeSeries(
463+
timeSeries.getLabelValues(),
464+
Value.longValue(count),
465+
pointTimestamp,
466+
timeSeriesTimestamp,
467+
summaryCountTimeSeries);
468+
}
469+
Double sum = summary.getSum();
470+
if (sum != null) {
471+
createTimeSeries(
472+
timeSeries.getLabelValues(),
473+
Value.doubleValue(sum),
474+
pointTimestamp,
475+
timeSeriesTimestamp,
476+
summarySumTimeSeries);
477+
}
478+
Snapshot snapshot = summary.getSnapshot();
479+
for (ValueAtPercentile valueAtPercentile : snapshot.getValueAtPercentiles()) {
480+
labelValuesWithPercentile.add(
481+
LabelValue.create(valueAtPercentile.getPercentile() + ""));
482+
createTimeSeries(
483+
labelValuesWithPercentile,
484+
Value.doubleValue(valueAtPercentile.getValue()),
485+
pointTimestamp,
486+
null,
487+
percentileTimeSeries);
488+
labelValuesWithPercentile.remove(labelValuesWithPercentile.size() - 1);
489+
}
490+
return null;
491+
}
492+
},
493+
Functions.<Void>returnNull());
494+
}
495+
}
496+
497+
// Metric for summary->count.
498+
if (summaryCountTimeSeries.size() > 0) {
499+
addMetric(
500+
metricsList,
501+
io.opencensus.metrics.export.MetricDescriptor.create(
502+
summaryMetric.getMetricDescriptor().getName() + SUMMARY_SUFFIX_COUNT,
503+
summaryMetric.getMetricDescriptor().getDescription(),
504+
"1",
505+
Type.CUMULATIVE_INT64,
506+
summaryMetric.getMetricDescriptor().getLabelKeys()),
507+
summaryCountTimeSeries);
508+
}
509+
510+
// Metric for summary->sum.
511+
if (summarySumTimeSeries.size() > 0) {
512+
addMetric(
513+
metricsList,
514+
io.opencensus.metrics.export.MetricDescriptor.create(
515+
summaryMetric.getMetricDescriptor().getName() + SUMMARY_SUFFIX_SUM,
516+
summaryMetric.getMetricDescriptor().getDescription(),
517+
summaryMetric.getMetricDescriptor().getUnit(),
518+
Type.CUMULATIVE_DOUBLE,
519+
summaryMetric.getMetricDescriptor().getLabelKeys()),
520+
summarySumTimeSeries);
521+
}
522+
523+
// Metric for summary->snapshot->percentiles.
524+
List<LabelKey> labelKeys = new ArrayList<>(summaryMetric.getMetricDescriptor().getLabelKeys());
525+
labelKeys.add(PERCENTILE_LABEL_KEY);
526+
addMetric(
527+
metricsList,
528+
io.opencensus.metrics.export.MetricDescriptor.create(
529+
summaryMetric.getMetricDescriptor().getName() + SNAPSHOT_SUFFIX_PERCENTILE,
530+
summaryMetric.getMetricDescriptor().getDescription(),
531+
summaryMetric.getMetricDescriptor().getUnit(),
532+
Type.GAUGE_DOUBLE,
533+
labelKeys),
534+
percentileTimeSeries);
535+
return metricsList;
536+
}
537+
538+
private static void addMetric(
539+
List<io.opencensus.metrics.export.Metric> metricsList,
540+
io.opencensus.metrics.export.MetricDescriptor metricDescriptor,
541+
List<io.opencensus.metrics.export.TimeSeries> timeSeriesList) {
542+
metricsList.add(io.opencensus.metrics.export.Metric.create(metricDescriptor, timeSeriesList));
543+
}
544+
545+
private static void createTimeSeries(
546+
List<LabelValue> labelValues,
547+
Value value,
548+
io.opencensus.common.Timestamp pointTimestamp,
549+
@javax.annotation.Nullable io.opencensus.common.Timestamp timeSeriesTimestamp,
550+
List<io.opencensus.metrics.export.TimeSeries> timeSeriesList) {
551+
timeSeriesList.add(
552+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
553+
labelValues,
554+
io.opencensus.metrics.export.Point.create(value, pointTimestamp),
555+
timeSeriesTimestamp));
556+
}
557+
428558
private static Map<String, String> getGcpResourceLabelsMappings() {
429559
Map<String, String> resourceLabels = new LinkedHashMap<String, String>();
430560
resourceLabels.put("project_id", STACKDRIVER_PROJECT_ID_KEY);

exporters/stats/stackdriver/src/main/java/io/opencensus/exporter/stats/stackdriver/StackdriverExporterWorker.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import io.opencensus.common.Duration;
3131
import io.opencensus.common.Scope;
3232
import io.opencensus.metrics.export.Metric;
33+
import io.opencensus.metrics.export.MetricDescriptor.Type;
3334
import io.opencensus.metrics.export.MetricProducer;
3435
import io.opencensus.metrics.export.MetricProducerManager;
3536
import io.opencensus.trace.EndSpanOptions;
@@ -172,8 +173,20 @@ void export() {
172173
Collection<Metric> producerMetricsList = metricProducer.getMetrics();
173174
metricsList.ensureCapacity(metricsList.size() + producerMetricsList.size());
174175
for (Metric metric : producerMetricsList) {
175-
if (registerMetricDescriptor(metric.getMetricDescriptor())) {
176-
metricsList.add(metric);
176+
final io.opencensus.metrics.export.MetricDescriptor metricDescriptor =
177+
metric.getMetricDescriptor();
178+
if (metricDescriptor.getType() == Type.SUMMARY) {
179+
List<Metric> convertedMetrics = StackdriverExportUtils.convertSummaryMetric(metric);
180+
metricsList.ensureCapacity(metricsList.size() + convertedMetrics.size());
181+
for (Metric convertedMetric : convertedMetrics) {
182+
if (registerMetricDescriptor(convertedMetric.getMetricDescriptor())) {
183+
metricsList.add(convertedMetric);
184+
}
185+
}
186+
} else {
187+
if (registerMetricDescriptor(metricDescriptor)) {
188+
metricsList.add(metric);
189+
}
177190
}
178191
}
179192
}

exporters/stats/stackdriver/src/test/java/io/opencensus/exporter/stats/stackdriver/StackdriverExportUtilsTest.java

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717
package io.opencensus.exporter.stats.stackdriver;
1818

1919
import static com.google.common.truth.Truth.assertThat;
20+
import static io.opencensus.exporter.stats.stackdriver.StackdriverExportUtils.PERCENTILE_LABEL_KEY;
21+
import static io.opencensus.exporter.stats.stackdriver.StackdriverExportUtils.SNAPSHOT_SUFFIX_PERCENTILE;
2022
import static io.opencensus.exporter.stats.stackdriver.StackdriverExportUtils.STACKDRIVER_PROJECT_ID_KEY;
23+
import static io.opencensus.exporter.stats.stackdriver.StackdriverExportUtils.SUMMARY_SUFFIX_COUNT;
24+
import static io.opencensus.exporter.stats.stackdriver.StackdriverExportUtils.SUMMARY_SUFFIX_SUM;
2125
import static io.opencensus.exporter.stats.stackdriver.StackdriverExporterWorker.CUSTOM_OPENCENSUS_DOMAIN;
2226
import static io.opencensus.exporter.stats.stackdriver.StackdriverExporterWorker.DEFAULT_DISPLAY_NAME_PREFIX;
2327

@@ -138,6 +142,47 @@ public class StackdriverExportUtilsTest {
138142
private static final io.opencensus.metrics.export.Metric DISTRIBUTION_METRIC =
139143
io.opencensus.metrics.export.Metric.createWithOneTimeSeries(
140144
HISTOGRAM_METRIC_DESCRIPTOR, DISTRIBUTION_TIME_SERIES);
145+
private static final Summary SUMMARY_1 =
146+
Summary.create(
147+
22L,
148+
74.8,
149+
Snapshot.create(
150+
10L,
151+
87.07,
152+
Arrays.asList(
153+
ValueAtPercentile.create(50, 6),
154+
ValueAtPercentile.create(75, 10.2),
155+
ValueAtPercentile.create(98, 4.6),
156+
ValueAtPercentile.create(99, 1.2))));
157+
private static final Value SUMMARY_VALUE_1 = Value.summaryValue(SUMMARY_1);
158+
private static final Point SUMMARY_POINT = Point.create(SUMMARY_VALUE_1, TIMESTAMP);
159+
private static final Summary SUMMARY_NULL_SUM =
160+
Summary.create(
161+
22L,
162+
null,
163+
Snapshot.create(10L, 87.07, Collections.singletonList(ValueAtPercentile.create(50, 6))));
164+
private static final Value SUMMARY_VALUE_NULL_SUM = Value.summaryValue(SUMMARY_NULL_SUM);
165+
private static final Point SUMMARY_POINT_NULL_SUM =
166+
Point.create(SUMMARY_VALUE_NULL_SUM, TIMESTAMP);
167+
private static final io.opencensus.metrics.export.TimeSeries SUMMARY_TIME_SERIES_NULL_SUM =
168+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
169+
LABEL_VALUE, SUMMARY_POINT_NULL_SUM, null);
170+
private static final io.opencensus.metrics.export.MetricDescriptor SUMMARY_METRIC_DESCRIPTOR =
171+
io.opencensus.metrics.export.MetricDescriptor.create(
172+
METRIC_NAME,
173+
METRIC_DESCRIPTION,
174+
METRIC_UNIT,
175+
io.opencensus.metrics.export.MetricDescriptor.Type.SUMMARY,
176+
LABEL_KEY);
177+
private static final io.opencensus.metrics.export.TimeSeries SUMMARY_TIME_SERIES =
178+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(LABEL_VALUE, SUMMARY_POINT, null);
179+
180+
private static final io.opencensus.metrics.export.Metric SUMMARY_METRIC =
181+
io.opencensus.metrics.export.Metric.createWithOneTimeSeries(
182+
SUMMARY_METRIC_DESCRIPTOR, SUMMARY_TIME_SERIES);
183+
private static final io.opencensus.metrics.export.Metric SUMMARY_METRIC_NULL_SUM =
184+
io.opencensus.metrics.export.Metric.createWithOneTimeSeries(
185+
SUMMARY_METRIC_DESCRIPTOR, SUMMARY_TIME_SERIES_NULL_SUM);
141186

142187
@Test
143188
public void createLabelDescriptor() {
@@ -461,6 +506,121 @@ public void createTimeSeriesList_withCustomMonitoredResource() {
461506
.build());
462507
}
463508

509+
@Test
510+
public void convertSummaryMetric() {
511+
io.opencensus.metrics.export.MetricDescriptor expectedMetricDescriptor1 =
512+
io.opencensus.metrics.export.MetricDescriptor.create(
513+
METRIC_NAME + SUMMARY_SUFFIX_COUNT,
514+
METRIC_DESCRIPTION,
515+
METRIC_UNIT_2,
516+
Type.CUMULATIVE_INT64,
517+
LABEL_KEY);
518+
io.opencensus.metrics.export.MetricDescriptor expectedMetricDescriptor2 =
519+
io.opencensus.metrics.export.MetricDescriptor.create(
520+
METRIC_NAME + SUMMARY_SUFFIX_SUM,
521+
METRIC_DESCRIPTION,
522+
METRIC_UNIT,
523+
Type.CUMULATIVE_DOUBLE,
524+
LABEL_KEY);
525+
List<LabelKey> labelKeys = new ArrayList<>(LABEL_KEY);
526+
labelKeys.add(PERCENTILE_LABEL_KEY);
527+
io.opencensus.metrics.export.MetricDescriptor expectedMetricDescriptor3 =
528+
io.opencensus.metrics.export.MetricDescriptor.create(
529+
METRIC_NAME + SNAPSHOT_SUFFIX_PERCENTILE,
530+
METRIC_DESCRIPTION,
531+
METRIC_UNIT,
532+
Type.GAUGE_DOUBLE,
533+
labelKeys);
534+
List<io.opencensus.metrics.export.TimeSeries> expectedTimeSeries1 =
535+
Collections.singletonList(
536+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
537+
LABEL_VALUE, Point.create(Value.longValue(22), TIMESTAMP), null));
538+
List<io.opencensus.metrics.export.TimeSeries> expectedTimeSeries2 =
539+
Collections.singletonList(
540+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
541+
LABEL_VALUE, Point.create(Value.doubleValue(74.8), TIMESTAMP), null));
542+
LabelValue existingLabelValues = LABEL_VALUE.get(0);
543+
List<io.opencensus.metrics.export.TimeSeries> expectedTimeSeries3 =
544+
Arrays.asList(
545+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
546+
Arrays.asList(existingLabelValues, LabelValue.create("50.0")),
547+
Point.create(Value.doubleValue(6), TIMESTAMP),
548+
null),
549+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
550+
Arrays.asList(existingLabelValues, LabelValue.create("75.0")),
551+
Point.create(Value.doubleValue(10.2), TIMESTAMP),
552+
null),
553+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
554+
Arrays.asList(existingLabelValues, LabelValue.create("98.0")),
555+
Point.create(Value.doubleValue(4.6), TIMESTAMP),
556+
null),
557+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
558+
Arrays.asList(existingLabelValues, LabelValue.create("99.0")),
559+
Point.create(Value.doubleValue(1.2), TIMESTAMP),
560+
null));
561+
List<io.opencensus.metrics.export.Metric> metrics =
562+
StackdriverExportUtils.convertSummaryMetric(SUMMARY_METRIC);
563+
564+
assertThat(metrics).isNotEmpty();
565+
assertThat(metrics.size()).isEqualTo(3);
566+
assertThat(metrics.get(0).getMetricDescriptor()).isEqualTo(expectedMetricDescriptor1);
567+
assertThat(metrics.get(0).getTimeSeriesList()).isNotEmpty();
568+
assertThat(metrics.get(0).getTimeSeriesList().size()).isEqualTo(1);
569+
assertThat(metrics.get(0).getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeries1);
570+
assertThat(metrics.get(1).getTimeSeriesList().size()).isEqualTo(1);
571+
assertThat(metrics.get(1).getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeries2);
572+
assertThat(metrics.get(1).getTimeSeriesList()).isNotEmpty();
573+
assertThat(metrics.get(1).getMetricDescriptor()).isEqualTo(expectedMetricDescriptor2);
574+
assertThat(metrics.get(2).getTimeSeriesList()).isNotEmpty();
575+
assertThat(metrics.get(2).getMetricDescriptor()).isEqualTo(expectedMetricDescriptor3);
576+
assertThat(metrics.get(2).getTimeSeriesList().size()).isEqualTo(4);
577+
assertThat(metrics.get(2).getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeries3);
578+
}
579+
580+
@Test
581+
public void convertSummaryMetricWithNullSum() {
582+
io.opencensus.metrics.export.MetricDescriptor expectedMetricDescriptor1 =
583+
io.opencensus.metrics.export.MetricDescriptor.create(
584+
METRIC_NAME + SUMMARY_SUFFIX_COUNT,
585+
METRIC_DESCRIPTION,
586+
METRIC_UNIT_2,
587+
Type.CUMULATIVE_INT64,
588+
LABEL_KEY);
589+
List<LabelKey> labelKeys = new ArrayList<>(LABEL_KEY);
590+
labelKeys.add(PERCENTILE_LABEL_KEY);
591+
io.opencensus.metrics.export.MetricDescriptor expectedMetricDescriptor2 =
592+
io.opencensus.metrics.export.MetricDescriptor.create(
593+
METRIC_NAME + SNAPSHOT_SUFFIX_PERCENTILE,
594+
METRIC_DESCRIPTION,
595+
METRIC_UNIT,
596+
Type.GAUGE_DOUBLE,
597+
labelKeys);
598+
List<io.opencensus.metrics.export.TimeSeries> expectedTimeSeries1 =
599+
Collections.singletonList(
600+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
601+
LABEL_VALUE, Point.create(Value.longValue(22), TIMESTAMP), null));
602+
LabelValue existingLabelValues = LABEL_VALUE.get(0);
603+
List<io.opencensus.metrics.export.TimeSeries> expectedTimeSeries2 =
604+
Collections.singletonList(
605+
io.opencensus.metrics.export.TimeSeries.createWithOnePoint(
606+
Arrays.asList(existingLabelValues, LabelValue.create("50.0")),
607+
Point.create(Value.doubleValue(6), TIMESTAMP),
608+
null));
609+
List<io.opencensus.metrics.export.Metric> metrics =
610+
StackdriverExportUtils.convertSummaryMetric(SUMMARY_METRIC_NULL_SUM);
611+
612+
assertThat(metrics).isNotEmpty();
613+
assertThat(metrics.size()).isEqualTo(2);
614+
assertThat(metrics.get(0).getMetricDescriptor()).isEqualTo(expectedMetricDescriptor1);
615+
assertThat(metrics.get(0).getTimeSeriesList()).isNotEmpty();
616+
assertThat(metrics.get(0).getTimeSeriesList().size()).isEqualTo(1);
617+
assertThat(metrics.get(0).getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeries1);
618+
assertThat(metrics.get(1).getTimeSeriesList()).isNotEmpty();
619+
assertThat(metrics.get(1).getMetricDescriptor()).isEqualTo(expectedMetricDescriptor2);
620+
assertThat(metrics.get(1).getTimeSeriesList().size()).isEqualTo(1);
621+
assertThat(metrics.get(1).getTimeSeriesList()).containsExactlyElementsIn(expectedTimeSeries2);
622+
}
623+
464624
@Test
465625
public void setResourceForBuilder_GcpInstanceType() {
466626
MonitoredResource.Builder monitoredResourceBuilder = DEFAULT_RESOURCE_WITH_PROJECT_ID.clone();

0 commit comments

Comments
 (0)