Skip to content

Full POC for metrics system #1844

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

Closed
wants to merge 1 commit into from
Closed
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
5 changes: 5 additions & 0 deletions codegen/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
<artifactId>http-client-spi</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>metrics-spi</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>regions</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
import software.amazon.awssdk.core.client.handler.SyncClientHandler;
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRefreshCache;
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
import software.amazon.awssdk.metrics.MetricCollector;

//TODO Make SyncClientClass extend SyncClientInterface (similar to what we do in AsyncClientClass)
public class SyncClientClass implements ClassSpec {
Expand Down Expand Up @@ -186,7 +187,18 @@ private List<MethodSpec> operationMethodSpecs(OperationModel opModel) {
method.endControlFlow();
}

method.addCode(protocolSpec.executionHandler(opModel));
String metricCollectorName = "apiCallMetricCollector";

method.addStatement("$1T $2N = $1T.create($3S)",
MetricCollector.class, metricCollectorName, "ApiCall");

method.beginControlFlow("try")
.addCode(protocolSpec.executionHandler(opModel))
.endControlFlow()
.beginControlFlow("finally")
.addStatement("clientConfiguration.option($T.$L).publish($N.collect())",
SdkClientOption.class, "METRIC_PUBLISHER", metricCollectorName)
.endControlFlow();

methods.add(method.build());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ public CodeBlock executionHandler(OperationModel opModel) {
opModel.getOperationName(),
"responseHandler",
opModel.getInput().getVariableName());

codeBlock.add(".withMetricCollector($L)", "apiCallMetricCollector");

if (opModel.hasStreamingInput()) {
return codeBlock.add(".withRequestBody(requestBody)")
.add(".withMarshaller($L));", syncStreamingMarshaller(intermediateModel, opModel, marshaller))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRefreshCache;
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
import software.amazon.awssdk.core.http.HttpResponseHandler;
import software.amazon.awssdk.metrics.MetricCollector;
import software.amazon.awssdk.protocols.json.AwsJsonProtocol;
import software.amazon.awssdk.protocols.json.AwsJsonProtocolFactory;
import software.amazon.awssdk.protocols.json.BaseAwsJsonProtocolFactory;
Expand Down Expand Up @@ -99,12 +100,15 @@ public CompletableFuture<DescribeEndpointsResponse> describeEndpoints(DescribeEn
HttpResponseHandler<AwsServiceException> errorResponseHandler = createErrorResponseHandler(protocolFactory,
operationMetadata);

MetricCollector metricCollector = MetricCollector.create("ApiCall");

CompletableFuture<DescribeEndpointsResponse> executeFuture = clientHandler
.execute(new ClientExecutionParams<DescribeEndpointsRequest, DescribeEndpointsResponse>()
.withOperationName("DescribeEndpoints")
.withMarshaller(new DescribeEndpointsRequestMarshaller(protocolFactory))
.withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
.withInput(describeEndpointsRequest));
.withInput(describeEndpointsRequest)
.withMetricCollector(metricCollector));
return executeFuture;
} catch (Throwable t) {
return CompletableFutureUtils.failedFuture(t);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.metrics.SimpleMetricPublisher;
import software.amazon.awssdk.metrics.publisher.MetricPublisher;
import software.amazon.awssdk.profiles.ProfileFile;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.regions.ServiceMetadata;
Expand Down Expand Up @@ -141,6 +143,7 @@ protected final SdkClientConfiguration finalizeChildConfiguration(SdkClientConfi
.option(SdkClientOption.EXECUTION_INTERCEPTORS, addAwsInterceptors(configuration))
.option(AwsClientOption.SIGNING_REGION, resolveSigningRegion(configuration))
.option(SdkClientOption.RETRY_POLICY, resolveAwsRetryPolicy(configuration))
.option(SdkClientOption.METRIC_PUBLISHER, resolveMetricPublisher(configuration))
.build();
}

Expand Down Expand Up @@ -232,6 +235,16 @@ private RetryPolicy resolveAwsRetryPolicy(SdkClientConfiguration config) {
return AwsRetryPolicy.forRetryMode(retryMode);
}

private MetricPublisher resolveMetricPublisher(SdkClientConfiguration config) {
MetricPublisher metricPublisher = config.option(SdkClientOption.METRIC_PUBLISHER);

if (metricPublisher == null) {
metricPublisher = SimpleMetricPublisher.create();
}

return metricPublisher;
}

@Override
public final BuilderT region(Region region) {
clientConfiguration.option(AwsClientOption.AWS_REGION, region);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ static <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext
.build())
.executionAttributes(executionAttributes)
.signer(computeSigner(originalRequest, clientConfig))
.metricCollector(executionParams.getMetricCollector())
.build();
}

Expand Down
86 changes: 86 additions & 0 deletions core/metrics-spi/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>core</artifactId>
<groupId>software.amazon.awssdk</groupId>
<version>2.13.18-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>metrics-spi</artifactId>
<name>AWS Java SDK :: Metrics SPI</name>
<description> This is the base module for SDK metrics feature. It contains the interfaces used for metrics feature
that are used by other modules in the library.
</description>

<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>annotations</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>utils</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>test-utils</artifactId>
<version>${awsjavasdk.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.tomakehurst</groupId>
<artifactId>wiremock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>software.amazon.awssdk.metrics</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package software.amazon.awssdk.metrics;

import java.time.Duration;

/**
* The set of metrics collected by default by the SDK.
*/
public final class DefaultMetrics {
/**
* The duration of time it took to marshall an SDK request object to an HTTP request object.
*/
public static final SdkMetric<Duration> REQUEST_MARSHALLING_TIME = SdkMetric.of("RequestMarshallingTime", Duration.class, MetricCategory.DEFAULT);

/**
* The duration of time it took to sign a request.
* <p>
* Note: this metric is only present if the SDK client is configured to sign requests.
*/
public static final SdkMetric<Duration> REQUEST_SIGNING_TIME = SdkMetric.of("RequestSigningTime", Duration.class, MetricCategory.DEFAULT);

/**
* The duration of time between sending the HTTP request and when the HTTP response is received.
*/
public static final SdkMetric<Duration> REQUEST_EXECUTION_TIME = SdkMetric.of("RequestExecutionTime", Duration.class, MetricCategory.DEFAULT);

private DefaultMetrics() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.awssdk.metrics;

import software.amazon.awssdk.annotations.SdkPublicApi;

/**
* A enum class representing the different types of metric categories in the SDK.
* <p>
* A metric can be tagged with multiple categories. Clients can enable/disable metric collection
* at a {@link MetricCategory} level.
*/
@SdkPublicApi
public enum MetricCategory {

/**
* All metrics defined by the SDK are classified under this category at a minimum. If the metrics feature is enabled
* but the category to collect is not, only metrics that are classified under this category are collected by the SDK
*/
DEFAULT("default"),

/**
* Metrics collected at the http client level are classified under this category.
*/
HTTP_CLIENT("httpclient"),

/**
* Metrics specific to streaming, eventStream APIs are classified under this category.
*/
STREAMING("streaming"),

/**
* This is an umbrella category (provided for convenience) that records metrics belonging to every category
* defined in this enum. Clients who wish to collect lot of SDK metrics data should use this.
* <p>
* Note: Enabling this option is verbose and can be expensive based on the platform the metrics are uploaded to.
* Please make sure you need all this data before using this category.
*/
ALL("all")
Comment on lines +45 to +52
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like we have to treat "all" magically. Can't we just do MetricCategory.values() to get all of them?


;

private final String value;

MetricCategory(String value) {
this.value = value;
}

public String getValue() {
return value;
}

/**
* Create a {@link MetricCategory} from the given String value. This method is case insensitive.
*
* @param value the value to create the {@link MetricCategory} from
* @return A {@link MetricCategory} if the given {@link #value} matches one of the enum values.
* Otherwise throws {@link IllegalArgumentException}
*/
public static MetricCategory fromString(String value) {
for (MetricCategory mc : MetricCategory.values()) {
if (mc.value.equalsIgnoreCase(value)) {
return mc;
}
}

throw new IllegalArgumentException("MetricCategory cannot be created from value: " + value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package software.amazon.awssdk.metrics;

import java.util.Collection;
import java.util.List;
import java.util.Optional;

/**
* An immutable collection of metrics.
*/
public interface MetricCollection extends Iterable<MetricRecord<?>> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SdkIterable?

/**
* @return The name of this metric collection.
*/
String name();

/**
* Return all the values of the given metric.
*
* @param metric The metric.
* @param <T> The type of the value.
* @return All of the values of this metric.
*/
<T> List<T> getAllMetricValues(SdkMetric<T> metric);

/**
* Return the values of the given metric.
*
* @param metric The metric.
* @param <T> The type of the value.
* @return The value of this metric.
*/
<T> Optional<T> getMetricValue(SdkMetric<T> metric);

/**
* @return The child metric collections.
*/
Collection<MetricCollection> children();
}
Loading