Skip to content

Commit f59577b

Browse files
committed
Refactored MetricsDefinition to use a Builder and inherit an abstract Metric class
1 parent 2713006 commit f59577b

File tree

9 files changed

+283
-102
lines changed

9 files changed

+283
-102
lines changed

gradle.properties

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# The --add-exports flags work around a bug with spotless and JDK 17
2+
# https://github.com/diffplug/spotless/issues/834
3+
org.gradle.jvmargs=-Xmx2g \
4+
--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
5+
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
6+
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
7+
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
8+
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package software.amazon.cloudwatchlogs.emf.model;
18+
19+
import com.fasterxml.jackson.annotation.JsonInclude;
20+
import com.fasterxml.jackson.annotation.JsonProperty;
21+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
22+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
23+
import lombok.AccessLevel;
24+
import lombok.Getter;
25+
import lombok.NonNull;
26+
import lombok.Setter;
27+
import software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionFilter;
28+
import software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionSerializer;
29+
import software.amazon.cloudwatchlogs.emf.serializers.UnitDeserializer;
30+
import software.amazon.cloudwatchlogs.emf.serializers.UnitSerializer;
31+
32+
/** Abstract immutable (except for name) class that all Metrics are based on. */
33+
@Getter
34+
public abstract class Metric {
35+
@JsonProperty("Name")
36+
@Setter(AccessLevel.PROTECTED)
37+
@NonNull
38+
protected String name;
39+
40+
@JsonProperty("Unit")
41+
@JsonSerialize(using = UnitSerializer.class)
42+
@JsonDeserialize(using = UnitDeserializer.class)
43+
protected Unit unit;
44+
45+
@JsonProperty("StorageResolution")
46+
@JsonInclude(
47+
value = JsonInclude.Include.CUSTOM,
48+
valueFilter =
49+
StorageResolutionFilter.class) // Do not serialize when valueFilter is true
50+
@JsonSerialize(using = StorageResolutionSerializer.class)
51+
protected StorageResolution storageResolution;
52+
53+
/** @return the values stored by this metric. */
54+
abstract Object getValues();
55+
56+
/** @return the values of this metric formatted to be flushed */
57+
protected Object getFormattedValues() {
58+
return this.getValues();
59+
}
60+
61+
/**
62+
* Creates a Metric with the first {@code size} values of the current metric
63+
*
64+
* @param size the maximum size of the returned metric's values
65+
* @return a Metric with the first {@code size} values of the current metric.
66+
*/
67+
protected abstract Metric getMetricValuesUnderSize(int size);
68+
69+
/**
70+
* Creates a Metric all metrics after the first {@code size} values of the current metric. If
71+
* there are less than {@code size} values, null is returned.
72+
*
73+
* @param size the maximum size of the returned metric's values
74+
* @return a Metric with the all metrics after the first {@code size} values of the current
75+
* metric. If there are less than {@code size} values, null is returned.
76+
*/
77+
protected abstract Metric getMetricValuesOverSize(int size);
78+
79+
public static interface MetricBuilder {
80+
81+
/**
82+
* Adds a value to the metric.
83+
*
84+
* @param value the value to be added to this metric
85+
*/
86+
MetricBuilder addValue(double value);
87+
88+
/**
89+
* Builds the metric.
90+
*
91+
* @return the built metric
92+
*/
93+
Metric build();
94+
}
95+
}

src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricDefinition.java

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -17,69 +17,112 @@
1717
package software.amazon.cloudwatchlogs.emf.model;
1818

1919
import com.fasterxml.jackson.annotation.JsonIgnore;
20-
import com.fasterxml.jackson.annotation.JsonInclude;
21-
import com.fasterxml.jackson.annotation.JsonProperty;
22-
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
23-
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
2420
import java.util.ArrayList;
25-
import java.util.Arrays;
2621
import java.util.List;
27-
import lombok.AllArgsConstructor;
2822
import lombok.Getter;
2923
import lombok.NonNull;
30-
import lombok.Setter;
31-
import software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionFilter;
32-
import software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionSerializer;
33-
import software.amazon.cloudwatchlogs.emf.serializers.UnitDeserializer;
34-
import software.amazon.cloudwatchlogs.emf.serializers.UnitSerializer;
3524

3625
/** Represents the MetricDefinition of the EMF schema. */
37-
@AllArgsConstructor
38-
class MetricDefinition {
39-
@NonNull
40-
@Getter
41-
@JsonProperty("Name")
42-
private String name;
43-
44-
@Getter
45-
@JsonProperty("Unit")
46-
@JsonSerialize(using = UnitSerializer.class)
47-
@JsonDeserialize(using = UnitDeserializer.class)
48-
private Unit unit;
49-
50-
@Getter
51-
@Setter
52-
@JsonProperty("StorageResolution")
53-
@JsonInclude(
54-
value = JsonInclude.Include.CUSTOM,
55-
valueFilter =
56-
StorageResolutionFilter.class) // Do not serialize when valueFilter is true
57-
@JsonSerialize(using = StorageResolutionSerializer.class)
58-
public StorageResolution storageResolution;
59-
60-
@JsonIgnore @NonNull @Getter private List<Double> values;
61-
62-
MetricDefinition(String name) {
63-
this(name, Unit.NONE, StorageResolution.STANDARD, new ArrayList<>());
26+
public class MetricDefinition extends Metric {
27+
@JsonIgnore @Getter protected List<Double> values;
28+
29+
private MetricDefinition(
30+
@NonNull String name,
31+
Unit unit,
32+
StorageResolution storageResolution,
33+
List<Double> values) {
34+
this.unit = unit;
35+
this.storageResolution = storageResolution;
36+
this.values = values;
37+
this.name = name;
38+
}
39+
40+
MetricDefinition(Unit unit, StorageResolution storageResolution, List<Double> values) {
41+
this.unit = unit;
42+
this.storageResolution = storageResolution;
43+
this.values = values;
6444
}
6545

66-
MetricDefinition(String name, double value) {
67-
this(name, Unit.NONE, StorageResolution.STANDARD, value);
46+
@Override
47+
protected Metric getMetricValuesUnderSize(int size) {
48+
List<Double> subList = values.subList(0, Math.min(values.size(), size));
49+
MetricDefinition metric =
50+
MetricDefinition.builder()
51+
.unit(unit)
52+
.storageResolution(storageResolution)
53+
.values(subList)
54+
.build();
55+
metric.setName(name);
56+
return metric;
6857
}
6958

70-
MetricDefinition(String name, Unit unit, double value) {
71-
this(name, unit, StorageResolution.STANDARD, new ArrayList<>(Arrays.asList(value)));
59+
@Override
60+
protected Metric getMetricValuesOverSize(int size) {
61+
if (size > values.size()) {
62+
return null;
63+
}
64+
List<Double> subList = values.subList(size, values.size());
65+
MetricDefinition metric =
66+
MetricDefinition.builder()
67+
.name(name)
68+
.unit(unit)
69+
.storageResolution(storageResolution)
70+
.values(subList)
71+
.build();
72+
return metric;
7273
}
7374

74-
MetricDefinition(String name, StorageResolution storageResolution, double value) {
75-
this(name, Unit.NONE, storageResolution, new ArrayList<>(Arrays.asList(value)));
75+
public static MetricDefinitionBuilder builder() {
76+
return new MetricDefinitionBuilder();
7677
}
7778

78-
MetricDefinition(String name, Unit unit, StorageResolution storageResolution, double value) {
79-
this(name, unit, storageResolution, new ArrayList<>(Arrays.asList(value)));
79+
/**
80+
* @return the values of this metric, simplified to a double instead of a list if there is only
81+
* one value
82+
*/
83+
@Override
84+
protected Object getFormattedValues() {
85+
return values.size() == 1 ? values.get(0) : values;
8086
}
8187

82-
void addValue(double value) {
83-
values.add(value);
88+
public static class MetricDefinitionBuilder extends MetricDefinition
89+
implements Metric.MetricBuilder {
90+
public MetricDefinitionBuilder() {
91+
super(Unit.NONE, StorageResolution.STANDARD, new ArrayList<>());
92+
}
93+
94+
protected MetricDefinitionBuilder name(@NonNull String name) {
95+
this.name = name;
96+
return this;
97+
}
98+
99+
@Override
100+
public MetricDefinitionBuilder addValue(double value) {
101+
this.values.add(value);
102+
return this;
103+
}
104+
105+
public MetricDefinitionBuilder unit(Unit unit) {
106+
this.unit = unit;
107+
return this;
108+
}
109+
110+
public MetricDefinitionBuilder storageResolution(StorageResolution storageResolution) {
111+
this.storageResolution = storageResolution;
112+
return this;
113+
}
114+
115+
public MetricDefinitionBuilder values(@NonNull List<Double> values) {
116+
this.values = values;
117+
return this;
118+
}
119+
120+
@Override
121+
public MetricDefinition build() {
122+
if (name == null) {
123+
return new MetricDefinition(unit, storageResolution, values);
124+
}
125+
return new MetricDefinition(name, unit, storageResolution, values);
126+
}
84127
}
85128
}

src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricDirective.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.stream.Collectors;
2424
import lombok.*;
2525
import software.amazon.cloudwatchlogs.emf.exception.DimensionSetExceededException;
26+
import software.amazon.cloudwatchlogs.emf.exception.InvalidMetricException;
2627

2728
/** Represents the MetricDirective part of the EMF schema. */
2829
@AllArgsConstructor
@@ -32,7 +33,7 @@ class MetricDirective {
3233
@JsonProperty("Namespace")
3334
private String namespace;
3435

35-
@JsonIgnore @Setter @Getter @With private Map<String, MetricDefinition> metrics;
36+
@JsonIgnore @Setter @Getter @With private Map<String, Metric> metrics;
3637

3738
@JsonIgnore
3839
@Getter(AccessLevel.PROTECTED)
@@ -85,16 +86,25 @@ void putMetric(String key, double value, Unit unit, StorageResolution storageRes
8586
metrics.compute(
8687
key,
8788
(k, v) -> {
88-
if (v == null) return new MetricDefinition(key, unit, storageResolution, value);
89-
else {
90-
v.addValue(value);
89+
if (v == null) {
90+
MetricDefinition.MetricDefinitionBuilder builder =
91+
MetricDefinition.builder()
92+
.name(k)
93+
.unit(unit)
94+
.storageResolution(storageResolution)
95+
.addValue(value);
96+
return builder;
97+
} else if (v instanceof Metric.MetricBuilder) {
98+
((Metric.MetricBuilder) v).addValue(value);
9199
return v;
100+
} else {
101+
throw new InvalidMetricException("Metric already exists and is Immutable");
92102
}
93103
});
94104
}
95105

96106
@JsonProperty("Metrics")
97-
Collection<MetricDefinition> getAllMetrics() {
107+
Collection<Metric> getAllMetrics() {
98108
return metrics.values();
99109
}
100110

src/main/java/software/amazon/cloudwatchlogs/emf/model/MetricsContext.java

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -323,40 +323,29 @@ public List<String> serialize() throws JsonProcessingException {
323323
return Arrays.asList(this.rootNode.serialize());
324324
} else {
325325
List<RootNode> nodes = new ArrayList<>();
326-
Map<String, MetricDefinition> metrics = new HashMap<>();
327-
Queue<MetricDefinition> metricDefinitions =
328-
new LinkedList<>(rootNode.metrics().values());
329-
while (!metricDefinitions.isEmpty()) {
330-
MetricDefinition metric = metricDefinitions.poll();
326+
Map<String, Metric> metrics = new HashMap<>();
327+
Queue<Metric> metricQueue = new LinkedList<>(rootNode.metrics().values());
328+
while (!metricQueue.isEmpty()) {
329+
Metric metric = metricQueue.poll();
331330

332331
if (metrics.size() == Constants.MAX_METRICS_PER_EVENT
333332
|| metrics.containsKey(metric.getName())) {
334333
nodes.add(buildRootNode(metrics));
335334
metrics = new HashMap<>();
336335
}
337336

338-
if (metric.getValues().size() <= Constants.MAX_DATAPOINTS_PER_METRIC) {
339-
metrics.put(metric.getName(), metric);
340-
} else {
341-
metrics.put(
342-
metric.getName(),
343-
new MetricDefinition(
344-
metric.getName(),
345-
metric.getUnit(),
346-
metric.getStorageResolution(),
347-
metric.getValues()
348-
.subList(0, Constants.MAX_DATAPOINTS_PER_METRIC)));
349-
metricDefinitions.offer(
350-
new MetricDefinition(
351-
metric.getName(),
352-
metric.getUnit(),
353-
metric.getStorageResolution(),
354-
metric.getValues()
355-
.subList(
356-
Constants.MAX_DATAPOINTS_PER_METRIC,
357-
metric.getValues().size())));
337+
Metric overSizeMetric =
338+
metric.getMetricValuesOverSize(Constants.MAX_DATAPOINTS_PER_METRIC);
339+
Metric underSizeMetric =
340+
metric.getMetricValuesUnderSize(Constants.MAX_DATAPOINTS_PER_METRIC);
341+
342+
metrics.put(metric.getName(), underSizeMetric);
343+
344+
if (overSizeMetric != null) {
345+
metricQueue.offer(overSizeMetric);
358346
}
359347
}
348+
360349
if (!metrics.isEmpty()) {
361350
nodes.add(buildRootNode(metrics));
362351
}
@@ -368,7 +357,7 @@ public List<String> serialize() throws JsonProcessingException {
368357
}
369358
}
370359

371-
private RootNode buildRootNode(Map<String, MetricDefinition> metrics) {
360+
private RootNode buildRootNode(Map<String, Metric> metrics) {
372361
Metadata metadata = rootNode.getAws();
373362
MetricDirective md = metadata.getCloudWatchMetrics().get(0);
374363
Metadata clonedMetadata =
@@ -379,6 +368,8 @@ private RootNode buildRootNode(Map<String, MetricDefinition> metrics) {
379368
private boolean anyMetricWithTooManyDataPoints(RootNode node) {
380369
return node.metrics().values().stream()
381370
.anyMatch(
382-
metric -> metric.getValues().size() > Constants.MAX_DATAPOINTS_PER_METRIC);
371+
metric ->
372+
metric.getMetricValuesOverSize(Constants.MAX_DATAPOINTS_PER_METRIC)
373+
!= null);
383374
}
384375
}

0 commit comments

Comments
 (0)