Skip to content

Commit 83d46ca

Browse files
committed
Fixed a cloudwatch-metric-publisher issue where very small values would result in a failure calling cloudwatch.
1 parent 312d49a commit 83d46ca

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

metric-publishers/cloudwatch-metric-publisher/src/main/java/software/amazon/awssdk/metrics/publishers/cloudwatch/internal/transform/MetricCollectionAggregator.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ private MetricDatum detailedMetricDatum(Instant timeBucket,
150150
.limit(maxElements);
151151

152152
boundedMetrics.forEach(detailedMetrics -> {
153-
values.add(detailedMetrics.metricValue());
153+
values.add(MetricValueNormalizer.normalize(detailedMetrics.metricValue()));
154154
counts.add((double) detailedMetrics.metricCount());
155155
});
156156

@@ -167,9 +167,9 @@ private MetricDatum detailedMetricDatum(Instant timeBucket,
167167
private MetricDatum summaryMetricDatum(Instant timeBucket,
168168
SummaryMetricAggregator metric) {
169169
StatisticSet stats = StatisticSet.builder()
170-
.minimum(metric.min())
171-
.maximum(metric.max())
172-
.sum(metric.sum())
170+
.minimum(MetricValueNormalizer.normalize(metric.min()))
171+
.maximum(MetricValueNormalizer.normalize(metric.max()))
172+
.sum(MetricValueNormalizer.normalize(metric.sum()))
173173
.sampleCount((double) metric.count())
174174
.build();
175175
return MetricDatum.builder()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.metrics.publishers.cloudwatch.internal.transform;
17+
18+
import software.amazon.awssdk.annotations.SdkInternalApi;
19+
20+
@SdkInternalApi
21+
class MetricValueNormalizer {
22+
/**
23+
* Really small values (close to 0) result in CloudWatch failing with an "unsupported value" error. Make sure that we floor
24+
* those values to 0 to prevent that error.
25+
*/
26+
private static final double ZERO_THRESHOLD = 0.0001;
27+
28+
private MetricValueNormalizer() {
29+
}
30+
31+
/**
32+
* Normalizes a metric value so that it won't upset CloudWatch when it is uploaded.
33+
*/
34+
public static double normalize(double value) {
35+
if (value > ZERO_THRESHOLD) {
36+
return value;
37+
}
38+
39+
if (value < -ZERO_THRESHOLD) {
40+
return value;
41+
}
42+
43+
return 0;
44+
}
45+
}

metric-publishers/cloudwatch-metric-publisher/src/main/java/software/amazon/awssdk/metrics/publishers/cloudwatch/internal/transform/TimeBucketedMetrics.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ private void aggregateMetrics(MetricCollection metrics, Map<MetricAggregatorKey,
120120
MetricAggregatorKey aggregatorKey = new MetricAggregatorKey(metricRecord.metric(), dimensions);
121121
valueFor(metricRecord).ifPresent(metricValue -> {
122122
bucket.computeIfAbsent(aggregatorKey, m -> newAggregator(aggregatorKey))
123-
.addMetricValue(metricValue);
123+
.addMetricValue(MetricValueNormalizer.normalize(metricValue));
124124
});
125125
});
126126
}

metric-publishers/cloudwatch-metric-publisher/src/test/java/software/amazon/awssdk/metrics/publishers/cloudwatch/internal/transform/MetricCollectionAggregatorTest.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,55 @@ private void validateValuesCount(PutMetricDataRequest request, int valuesExpecte
8282
.hasSize(valuesExpected);
8383
}
8484

85+
@Test
86+
public void smallValuesAreNormalizedToZeroWithSummaryMetrics() {
87+
// Really small values (close to 0) result in CloudWatch failing with an "unsupported value" error. Make sure that we
88+
// floor those values to 0 to prevent that error.
89+
90+
MetricCollectionAggregator aggregator = defaultAggregator();
91+
92+
MetricCollector collector = collector();
93+
SdkMetric<Double> metric = someMetric(Double.class);
94+
collector.reportMetric(metric, -1E-10);
95+
collector.reportMetric(metric, 1E-10);
96+
aggregator.addCollection(collectToFixedTime(collector));
97+
98+
assertThat(aggregator.getRequests()).hasOnlyOneElementSatisfying(request -> {
99+
assertThat(request.metricData()).hasOnlyOneElementSatisfying(metricData -> {
100+
StatisticSet stats = metricData.statisticValues();
101+
assertThat(stats.minimum()).isEqualTo(0.0);
102+
assertThat(stats.maximum()).isEqualTo(0.0);
103+
assertThat(stats.sum()).isEqualTo(0.0);
104+
assertThat(stats.sampleCount()).isEqualTo(2.0);
105+
});
106+
});
107+
}
108+
109+
@Test
110+
public void smallValuesAreNormalizedToZeroWithDetailedMetrics() {
111+
// Really small values (close to 0) result in CloudWatch failing with an "unsupported value" error. Make sure that we
112+
// floor those values to 0 to prevent that error.
113+
114+
SdkMetric<Double> metric = someMetric(Double.class);
115+
MetricCollectionAggregator aggregator = aggregatorWithCustomDetailedMetrics(metric);
116+
117+
MetricCollector collector = collector();
118+
collector.reportMetric(metric, -1E-10);
119+
collector.reportMetric(metric, 1E-10);
120+
aggregator.addCollection(collectToFixedTime(collector));
121+
122+
assertThat(aggregator.getRequests()).hasOnlyOneElementSatisfying(request -> {
123+
assertThat(request.metricData()).hasOnlyOneElementSatisfying(metricData -> {
124+
assertThat(metricData.values()).hasOnlyOneElementSatisfying(metricValue -> {
125+
assertThat(metricValue).isEqualTo(0.0);
126+
});
127+
assertThat(metricData.counts()).hasOnlyOneElementSatisfying(metricCount -> {
128+
assertThat(metricCount).isEqualTo(2.0);
129+
});
130+
});
131+
});
132+
}
133+
85134
@Test
86135
public void dimensionOrderInCollectionDoesNotMatter() {
87136
MetricCollectionAggregator aggregator = defaultAggregator();

0 commit comments

Comments
 (0)