Skip to content

Feature: Adding Metric Aggregation in the form of a StatisticSet #150

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@
import software.amazon.cloudwatchlogs.emf.exception.InvalidMetricException;
import software.amazon.cloudwatchlogs.emf.exception.InvalidNamespaceException;
import software.amazon.cloudwatchlogs.emf.exception.InvalidTimestampException;
import software.amazon.cloudwatchlogs.emf.model.AggregationType;
import software.amazon.cloudwatchlogs.emf.model.DimensionSet;
import software.amazon.cloudwatchlogs.emf.model.Metric;
import software.amazon.cloudwatchlogs.emf.model.MetricsContext;
import software.amazon.cloudwatchlogs.emf.model.StorageResolution;
import software.amazon.cloudwatchlogs.emf.model.Unit;
Expand All @@ -45,6 +47,7 @@ public class MetricsLogger {
private MetricsContext context;
private CompletableFuture<Environment> environmentFuture;
private EnvironmentProvider environmentProvider;
@Getter @Setter private volatile AggregationType defaultAggregationType = AggregationType.LIST;
/**
* This lock is used to create an internal sync context for flush() method in multi-threaded
* situations. Flush() acquires write lock, other methods (accessing mutable shared data with
Expand Down Expand Up @@ -191,74 +194,177 @@ public MetricsLogger resetDimensions(boolean useDefault) {
* @param value is the value of the metric
* @param unit is the unit of the metric value
* @param storageResolution is the resolution of the metric
* @param aggregationType is the aggregation type of the metric
* @see <a
* href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html#high-resolution-metrics">CloudWatch
* High Resolution Metrics</a>
* @return the current logger
* @throws InvalidMetricException if the metric is invalid
*/
public MetricsLogger putMetric(
String key, double value, Unit unit, StorageResolution storageResolution)
String key,
double value,
Unit unit,
StorageResolution storageResolution,
AggregationType aggregationType)
throws InvalidMetricException {
rwl.readLock().lock();
try {
this.context.putMetric(key, value, unit, storageResolution);
this.context.putMetric(key, value, unit, storageResolution, aggregationType);
return this;
} finally {
rwl.readLock().unlock();
}
}

/**
* Put a metric value. This value will be emitted to CloudWatch Metrics asynchronously and does
* not contribute to your account TPS limits. The value will also be available in your
* CloudWatch Logs
*
* @param key is the name of the metric
* @param value is the value of the metric
* @param storageResolution is the resolution of the metric
* @see <a
* href="https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html#high-resolution-metrics">CloudWatch
* High Resolution Metrics</a>
* @return the current logger
* @throws InvalidMetricException if the metric is invalid
*
* <ul>
* <li>Unit defaults to NONE
* <li>AggregationType defaults to the default AggregationType of this logger
* </ul>
*
* @see MetricsLogger#putMetric(String key, double value, Unit unit, StorageResolution
* storageResolution, AggregationType aggregationType) putMetric(String key, double value,
* Unit unit, StorageResolution storageResolution, AggregationType aggregationType)
*/
public MetricsLogger putMetric(String key, double value, StorageResolution storageResolution)
throws InvalidMetricException {
this.putMetric(key, value, Unit.NONE, storageResolution);
this.putMetric(key, value, Unit.NONE, storageResolution, this.defaultAggregationType);
return this;
}

/**
* Put a metric value. This value will be emitted to CloudWatch Metrics asynchronously and does
* not contribute to your account TPS limits. The value will also be available in your
* CloudWatch Logs
*
* @param key is the name of the metric
* @param value is the value of the metric
* @param unit is the unit of the metric value
* @return the current logger
* @throws InvalidMetricException if the metric is invalid
*
* <ul>
* <li>StorageResolution defaults to STANDARD
* <li>AggregationType defaults to the default AggregationType of this logger
* </ul>
*
* @see MetricsLogger#putMetric(String key, double value, Unit unit, StorageResolution
* storageResolution, AggregationType aggregationType) putMetric(String key, double value,
* Unit unit, StorageResolution storageResolution, AggregationType aggregationType)
*/
public MetricsLogger putMetric(String key, double value, Unit unit)
throws InvalidMetricException {
this.putMetric(key, value, unit, StorageResolution.STANDARD);
this.putMetric(key, value, unit, StorageResolution.STANDARD, this.defaultAggregationType);
return this;
}

/**
* Put a metric value. This value will be emitted to CloudWatch Metrics asynchronously and does
* not contribute to your account TPS limits. The value will also be available in your
* CloudWatch Logs
*
*
* <ul>
* <li>StorageResolution defaults to STANDARD
* <li>Unit defaults to NONE
* <li>AggregationType defaults to the default AggregationType of this logger
* </ul>
*
* @see MetricsLogger#putMetric(String key, double value, Unit unit, StorageResolution
* storageResolution, AggregationType aggregationType) putMetric(String key, double value,
* Unit unit, StorageResolution storageResolution, AggregationType aggregationType)
*/
public MetricsLogger putMetric(String key, double value) throws InvalidMetricException {
this.putMetric(
key, value, Unit.NONE, StorageResolution.STANDARD, this.defaultAggregationType);
return this;
}

/**
*
*
* <ul>
* <li>AggregationType defaults to the default AggregationType of this logger
* </ul>
*
* @see MetricsLogger#putMetric(String key, double value, Unit unit, StorageResolution
* storageResolution, AggregationType aggregationType) putMetric(String key, double value,
* Unit unit, StorageResolution storageResolution, AggregationType aggregationType)
*/
public MetricsLogger putMetric(
String key, double value, Unit unit, StorageResolution storageResolution)
throws InvalidMetricException {
this.putMetric(key, value, Unit.NONE, storageResolution, this.defaultAggregationType);
return this;
}

/**
*
*
* <ul>
* <li>StorageResolution defaults to STANDARD
* <li>AggregationType defaults to the default AggregationType of this logger
* </ul>
*
* @see MetricsLogger#putMetric(String key, double value, Unit unit, StorageResolution
* storageResolution, AggregationType aggregationType) putMetric(String key, double value,
* Unit unit, StorageResolution storageResolution, AggregationType aggregationType)
*/
public MetricsLogger putMetric(
String key,
double value,
StorageResolution storageResolution,
AggregationType aggregationType)
throws InvalidMetricException {
this.putMetric(key, value, Unit.NONE, storageResolution, aggregationType);
return this;
}

/**
*
*
* <ul>
* <li>StorageResolution defaults to STANDARD
* </ul>
*
* @see MetricsLogger#putMetric(String key, double value, Unit unit, StorageResolution
* storageResolution, AggregationType aggregationType) putMetric(String key, double value,
* Unit unit, StorageResolution storageResolution, AggregationType aggregationType)
*/
public MetricsLogger putMetric(
String key, double value, Unit unit, AggregationType aggregationType)
throws InvalidMetricException {
this.putMetric(key, value, unit, StorageResolution.STANDARD, aggregationType);
return this;
}

/**
*
*
* <ul>
* <li>StorageResolution defaults to STANDARD
* <li>Unit defaults to NONE
* </ul>
*
* @see MetricsLogger#putMetric(String key, double value, Unit unit, StorageResolution
* storageResolution, AggregationType aggregationType) putMetric(String key, double value,
* Unit unit, StorageResolution storageResolution, AggregationType aggregationType)
*/
public MetricsLogger putMetric(String key, double value, AggregationType aggregationType)
throws InvalidMetricException {
this.putMetric(key, value, Unit.NONE, StorageResolution.STANDARD, aggregationType);
return this;
}

/**
* Set a metric value, if a metric already has the same key it will be overwitten. This value
* will be emitted to CloudWatch Metrics asynchronously and does not contribute to your account
* TPS limits. The value will also be available in your CloudWatch Logs
*
* @param key the name of the metric
* @param value the value of the metric
* @return the current logger
* @throws InvalidMetricException if the metric is invalid
*/
public MetricsLogger putMetric(String key, double value) throws InvalidMetricException {
this.putMetric(key, value, Unit.NONE, StorageResolution.STANDARD);
return this;
public MetricsLogger setMetric(String key, Metric value) throws InvalidMetricException {
try {
this.context.setMetric(key, value);
return this;
} finally {
rwl.readLock().unlock();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package software.amazon.cloudwatchlogs.emf.model;

public enum AggregationType {
LIST(0),
STATISTIC_SET(1),
UNKNOWN_TO_SDK_VERSION(-1);

private final int value;

AggregationType(final int newValue) {
value = newValue;
}

public int getValue() {
return this.value;
}
}
46 changes: 26 additions & 20 deletions src/main/java/software/amazon/cloudwatchlogs/emf/model/Metric.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.util.LinkedList;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NonNull;
Expand All @@ -41,15 +42,15 @@ public abstract class Metric<V> {
@JsonProperty("Unit")
@JsonSerialize(using = UnitSerializer.class)
@JsonDeserialize(using = UnitDeserializer.class)
protected Unit unit;
protected Unit unit = Unit.NONE;

@JsonProperty("StorageResolution")
@JsonInclude(
value = JsonInclude.Include.CUSTOM,
valueFilter =
StorageResolutionFilter.class) // Do not serialize when valueFilter is true
@JsonSerialize(using = StorageResolutionSerializer.class)
protected StorageResolution storageResolution;
protected StorageResolution storageResolution = StorageResolution.STANDARD;

@JsonIgnore @Getter protected V values;

Expand All @@ -58,23 +59,20 @@ protected Object getFormattedValues() {
return this.getValues();
}

/**
* Creates a Metric with the first {@code size} values of the current metric
*
* @param size the maximum size of the returned metric's values
* @return a Metric with the first {@code size} values of the current metric.
*/
protected abstract Metric getMetricValuesUnderSize(int size);
/** @return true if the values of this metric are valid, false otherwise. */
public abstract boolean hasValidValues();

/** @return true if the values of this metric are oversized for CloudWatch Logs */
protected abstract boolean isOversized();

/**
* Creates a Metric all metrics after the first {@code size} values of the current metric. If
* there are less than {@code size} values, null is returned.
* Creates a list of new Metrics based off the values in this metric split in so that they are
* small enough that CWL will not drop the message values
*
* @param size the maximum size of the returned metric's values
* @return a Metric with the all metrics after the first {@code size} values of the current
* metric. If there are less than {@code size} values, null is returned.
* @return a list of metrics based off of the values of this metric that aren't too large for
* CWL
*/
protected abstract Metric getMetricValuesOverSize(int size);
protected abstract LinkedList<Metric> serialize();

public abstract static class MetricBuilder<V, T extends MetricBuilder<V, T>> extends Metric<V> {

Expand All @@ -92,7 +90,7 @@ public abstract static class MetricBuilder<V, T extends MetricBuilder<V, T>> ext
*
* @return the built metric
*/
abstract Metric build();
abstract Metric<V> build();

protected T name(@NonNull String name) {
this.name = name;
Expand All @@ -109,16 +107,24 @@ public T storageResolution(StorageResolution storageResolution) {
return getThis();
}

protected Metric getMetricValuesOverSize(int size) {
return build().getMetricValuesOverSize(size);
@Override
public boolean hasValidValues() {
return build().hasValidValues();
}

protected Metric getMetricValuesUnderSize(int size) {
return build().getMetricValuesUnderSize(size);
@Override
protected LinkedList<Metric> serialize() {
return build().serialize();
}

@Override
protected Object getFormattedValues() {
return build().getFormattedValues();
}

@Override
protected boolean isOversized() {
return build().isOversized();
}
}
}
Loading