Skip to content

Commit 51e2337

Browse files
authored
Refactored MetricsDefinition to use a Builder and inherit an abstract Metric class (#149)
1 parent 2713006 commit 51e2337

File tree

9 files changed

+304
-108
lines changed

9 files changed

+304
-108
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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.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;
24+
import lombok.AccessLevel;
25+
import lombok.Getter;
26+
import lombok.NonNull;
27+
import lombok.Setter;
28+
import software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionFilter;
29+
import software.amazon.cloudwatchlogs.emf.serializers.StorageResolutionSerializer;
30+
import software.amazon.cloudwatchlogs.emf.serializers.UnitDeserializer;
31+
import software.amazon.cloudwatchlogs.emf.serializers.UnitSerializer;
32+
33+
/** Abstract immutable (except for name) class that all Metrics are based on. */
34+
@Getter
35+
public abstract class Metric<V> {
36+
@JsonProperty("Name")
37+
@Setter(AccessLevel.PROTECTED)
38+
@NonNull
39+
protected String name;
40+
41+
@JsonProperty("Unit")
42+
@JsonSerialize(using = UnitSerializer.class)
43+
@JsonDeserialize(using = UnitDeserializer.class)
44+
protected Unit unit;
45+
46+
@JsonProperty("StorageResolution")
47+
@JsonInclude(
48+
value = JsonInclude.Include.CUSTOM,
49+
valueFilter =
50+
StorageResolutionFilter.class) // Do not serialize when valueFilter is true
51+
@JsonSerialize(using = StorageResolutionSerializer.class)
52+
protected StorageResolution storageResolution;
53+
54+
@JsonIgnore @Getter protected V values;
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 abstract static class MetricBuilder<V, T extends MetricBuilder<V, T>> extends Metric<V> {
80+
81+
protected abstract T getThis();
82+
83+
/**
84+
* Adds a value to the metric.
85+
*
86+
* @param value the value to be added to this metric
87+
*/
88+
abstract T addValue(double value);
89+
90+
/**
91+
* Builds the metric.
92+
*
93+
* @return the built metric
94+
*/
95+
abstract Metric build();
96+
97+
protected T name(@NonNull String name) {
98+
this.name = name;
99+
return getThis();
100+
}
101+
102+
public T unit(Unit unit) {
103+
this.unit = unit;
104+
return getThis();
105+
}
106+
107+
public T storageResolution(StorageResolution storageResolution) {
108+
this.storageResolution = storageResolution;
109+
return getThis();
110+
}
111+
112+
protected Metric getMetricValuesOverSize(int size) {
113+
return build().getMetricValuesOverSize(size);
114+
}
115+
116+
protected Metric getMetricValuesUnderSize(int size) {
117+
return build().getMetricValuesUnderSize(size);
118+
}
119+
120+
protected Object getFormattedValues() {
121+
return build().getFormattedValues();
122+
}
123+
}
124+
}

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

+79-48
Original file line numberDiff line numberDiff line change
@@ -16,70 +16,101 @@
1616

1717
package software.amazon.cloudwatchlogs.emf.model;
1818

19-
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;
2419
import java.util.ArrayList;
25-
import java.util.Arrays;
2620
import java.util.List;
27-
import lombok.AllArgsConstructor;
28-
import lombok.Getter;
2921
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;
3522

3623
/** Represents the MetricDefinition of the EMF schema. */
37-
@AllArgsConstructor
38-
class MetricDefinition {
39-
@NonNull
40-
@Getter
41-
@JsonProperty("Name")
42-
private String name;
24+
public class MetricDefinition extends Metric<List<Double>> {
4325

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;
26+
private MetricDefinition(
27+
@NonNull String name,
28+
Unit unit,
29+
StorageResolution storageResolution,
30+
List<Double> values) {
31+
this.unit = unit;
32+
this.storageResolution = storageResolution;
33+
this.values = values;
34+
this.name = name;
35+
}
6136

62-
MetricDefinition(String name) {
63-
this(name, Unit.NONE, StorageResolution.STANDARD, new ArrayList<>());
37+
MetricDefinition(Unit unit, StorageResolution storageResolution, List<Double> values) {
38+
this.unit = unit;
39+
this.storageResolution = storageResolution;
40+
this.values = values;
6441
}
6542

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

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

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

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

82-
void addValue(double value) {
83-
values.add(value);
85+
public static class MetricDefinitionBuilder
86+
extends Metric.MetricBuilder<List<Double>, MetricDefinitionBuilder> {
87+
88+
@Override
89+
protected MetricDefinitionBuilder getThis() {
90+
return this;
91+
}
92+
93+
public MetricDefinitionBuilder() {
94+
this.values = new ArrayList<>();
95+
}
96+
97+
@Override
98+
public MetricDefinitionBuilder addValue(double value) {
99+
this.values.add(value);
100+
return this;
101+
}
102+
103+
public MetricDefinitionBuilder values(@NonNull List<Double> values) {
104+
this.values = values;
105+
return this;
106+
}
107+
108+
@Override
109+
public MetricDefinition build() {
110+
if (name == null) {
111+
return new MetricDefinition(unit, storageResolution, values);
112+
}
113+
return new MetricDefinition(name, unit, storageResolution, values);
114+
}
84115
}
85116
}

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

+18-5
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,28 @@ 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(
102+
String.format(
103+
"New metrics cannot be put to the name: \"%s\", because it has been set to an immutable metric type.",
104+
k));
92105
}
93106
});
94107
}
95108

96109
@JsonProperty("Metrics")
97-
Collection<MetricDefinition> getAllMetrics() {
110+
Collection<Metric> getAllMetrics() {
98111
return metrics.values();
99112
}
100113

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

+18-27
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)