Skip to content

Commit 7ddad4a

Browse files
committed
Full POC for metrics system
1 parent b2b805f commit 7ddad4a

File tree

34 files changed

+1171
-7
lines changed

34 files changed

+1171
-7
lines changed

codegen/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
<artifactId>http-client-spi</artifactId>
5858
<version>${awsjavasdk.version}</version>
5959
</dependency>
60+
<dependency>
61+
<groupId>software.amazon.awssdk</groupId>
62+
<artifactId>metrics-spi</artifactId>
63+
<version>${awsjavasdk.version}</version>
64+
</dependency>
6065
<dependency>
6166
<groupId>software.amazon.awssdk</groupId>
6267
<artifactId>regions</artifactId>

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/SyncClientClass.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import software.amazon.awssdk.core.client.handler.SyncClientHandler;
5353
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRefreshCache;
5454
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
55+
import software.amazon.awssdk.metrics.MetricCollector;
5556

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

189-
method.addCode(protocolSpec.executionHandler(opModel));
190+
String metricCollectorName = "apiCallMetricCollector";
191+
192+
method.addStatement("$1T $2N = $1T.create($3S)",
193+
MetricCollector.class, metricCollectorName, "ApiCall");
194+
195+
method.beginControlFlow("try")
196+
.addCode(protocolSpec.executionHandler(opModel))
197+
.endControlFlow()
198+
.beginControlFlow("finally")
199+
.addStatement("clientConfiguration.option($T.$L).publish($N.collect())",
200+
SdkClientOption.class, "METRIC_PUBLISHER", metricCollectorName)
201+
.endControlFlow();
190202

191203
methods.add(method.build());
192204

codegen/src/main/java/software/amazon/awssdk/codegen/poet/client/specs/XmlProtocolSpec.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ public CodeBlock executionHandler(OperationModel opModel) {
120120
opModel.getOperationName(),
121121
"responseHandler",
122122
opModel.getInput().getVariableName());
123+
124+
codeBlock.add(".withMetricCollector($L)", "apiCallMetricCollector");
125+
123126
if (opModel.hasStreamingInput()) {
124127
return codeBlock.add(".withRequestBody(requestBody)")
125128
.add(".withMarshaller($L));", syncStreamingMarshaller(intermediateModel, opModel, marshaller))

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/test-endpoint-discovery-async.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRefreshCache;
1919
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
2020
import software.amazon.awssdk.core.http.HttpResponseHandler;
21+
import software.amazon.awssdk.metrics.MetricCollector;
2122
import software.amazon.awssdk.protocols.json.AwsJsonProtocol;
2223
import software.amazon.awssdk.protocols.json.AwsJsonProtocolFactory;
2324
import software.amazon.awssdk.protocols.json.BaseAwsJsonProtocolFactory;
@@ -99,12 +100,15 @@ public CompletableFuture<DescribeEndpointsResponse> describeEndpoints(DescribeEn
99100
HttpResponseHandler<AwsServiceException> errorResponseHandler = createErrorResponseHandler(protocolFactory,
100101
operationMetadata);
101102

103+
MetricCollector metricCollector = MetricCollector.create("ApiCall");
104+
102105
CompletableFuture<DescribeEndpointsResponse> executeFuture = clientHandler
103106
.execute(new ClientExecutionParams<DescribeEndpointsRequest, DescribeEndpointsResponse>()
104107
.withOperationName("DescribeEndpoints")
105108
.withMarshaller(new DescribeEndpointsRequestMarshaller(protocolFactory))
106109
.withResponseHandler(responseHandler).withErrorResponseHandler(errorResponseHandler)
107-
.withInput(describeEndpointsRequest));
110+
.withInput(describeEndpointsRequest)
111+
.withMetricCollector(metricCollector));
108112
return executeFuture;
109113
} catch (Throwable t) {
110114
return CompletableFutureUtils.failedFuture(t);

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import software.amazon.awssdk.core.retry.RetryPolicy;
3838
import software.amazon.awssdk.http.SdkHttpClient;
3939
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
40+
import software.amazon.awssdk.metrics.SimpleMetricPublisher;
41+
import software.amazon.awssdk.metrics.publisher.MetricPublisher;
4042
import software.amazon.awssdk.profiles.ProfileFile;
4143
import software.amazon.awssdk.regions.Region;
4244
import software.amazon.awssdk.regions.ServiceMetadata;
@@ -141,6 +143,7 @@ protected final SdkClientConfiguration finalizeChildConfiguration(SdkClientConfi
141143
.option(SdkClientOption.EXECUTION_INTERCEPTORS, addAwsInterceptors(configuration))
142144
.option(AwsClientOption.SIGNING_REGION, resolveSigningRegion(configuration))
143145
.option(SdkClientOption.RETRY_POLICY, resolveAwsRetryPolicy(configuration))
146+
.option(SdkClientOption.METRIC_PUBLISHER, resolveMetricPublisher(configuration))
144147
.build();
145148
}
146149

@@ -232,6 +235,16 @@ private RetryPolicy resolveAwsRetryPolicy(SdkClientConfiguration config) {
232235
return AwsRetryPolicy.forRetryMode(retryMode);
233236
}
234237

238+
private MetricPublisher resolveMetricPublisher(SdkClientConfiguration config) {
239+
MetricPublisher metricPublisher = config.option(SdkClientOption.METRIC_PUBLISHER);
240+
241+
if (metricPublisher == null) {
242+
metricPublisher = SimpleMetricPublisher.create();
243+
}
244+
245+
return metricPublisher;
246+
}
247+
235248
@Override
236249
public final BuilderT region(Region region) {
237250
clientConfiguration.option(AwsClientOption.AWS_REGION, region);

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ static <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext
9999
.build())
100100
.executionAttributes(executionAttributes)
101101
.signer(computeSigner(originalRequest, clientConfig))
102+
.metricCollector(executionParams.getMetricCollector())
102103
.build();
103104
}
104105

core/metrics-spi/pom.xml

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>core</artifactId>
7+
<groupId>software.amazon.awssdk</groupId>
8+
<version>2.11.12-SNAPSHOT</version>
9+
</parent>
10+
<modelVersion>4.0.0</modelVersion>
11+
12+
<artifactId>metrics-spi</artifactId>
13+
<name>AWS Java SDK :: Metrics SPI</name>
14+
<description> This is the base module for SDK metrics feature. It contains the interfaces used for metrics feature
15+
that are used by other modules in the library.
16+
</description>
17+
18+
<dependencies>
19+
<dependency>
20+
<groupId>com.fasterxml.jackson.core</groupId>
21+
<artifactId>jackson-databind</artifactId>
22+
<version>2.11.0</version>
23+
</dependency>
24+
<dependency>
25+
<groupId>software.amazon.awssdk</groupId>
26+
<artifactId>annotations</artifactId>
27+
<version>${awsjavasdk.version}</version>
28+
</dependency>
29+
<dependency>
30+
<groupId>software.amazon.awssdk</groupId>
31+
<artifactId>utils</artifactId>
32+
<version>${awsjavasdk.version}</version>
33+
</dependency>
34+
<dependency>
35+
<groupId>software.amazon.awssdk</groupId>
36+
<artifactId>test-utils</artifactId>
37+
<version>${awsjavasdk.version}</version>
38+
<scope>test</scope>
39+
</dependency>
40+
<dependency>
41+
<groupId>junit</groupId>
42+
<artifactId>junit</artifactId>
43+
<scope>test</scope>
44+
</dependency>
45+
<dependency>
46+
<groupId>com.github.tomakehurst</groupId>
47+
<artifactId>wiremock</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
<dependency>
51+
<groupId>org.assertj</groupId>
52+
<artifactId>assertj-core</artifactId>
53+
<scope>test</scope>
54+
</dependency>
55+
<dependency>
56+
<groupId>org.mockito</groupId>
57+
<artifactId>mockito-core</artifactId>
58+
<scope>test</scope>
59+
</dependency>
60+
</dependencies>
61+
62+
63+
<build>
64+
<plugins>
65+
<plugin>
66+
<groupId>org.apache.maven.plugins</groupId>
67+
<artifactId>maven-jar-plugin</artifactId>
68+
<configuration>
69+
<archive>
70+
<manifestEntries>
71+
<Automatic-Module-Name>software.amazon.awssdk.metrics</Automatic-Module-Name>
72+
</manifestEntries>
73+
</archive>
74+
</configuration>
75+
</plugin>
76+
<plugin>
77+
<groupId>org.apache.maven.plugins</groupId>
78+
<artifactId>maven-compiler-plugin</artifactId>
79+
<configuration>
80+
<source>1.8</source>
81+
<target>1.8</target>
82+
</configuration>
83+
</plugin>
84+
</plugins>
85+
</build>
86+
</project>
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package software.amazon.awssdk.metrics;
2+
3+
import java.time.Duration;
4+
5+
/**
6+
* The set of metrics collected by default by the SDK.
7+
*/
8+
public final class DefaultMetrics {
9+
/**
10+
* The duration of time it took to marshall an SDK request object to an HTTP request object.
11+
*/
12+
public static final SdkMetric<Duration> REQUEST_MARSHALLING_TIME = SdkMetric.of("RequestMarshallingTime", Duration.class, MetricCategory.DEFAULT);
13+
14+
/**
15+
* The duration of time it took to sign a request.
16+
* <p>
17+
* Note: this metric is only present if the SDK client is configured to sign requests.
18+
*/
19+
public static final SdkMetric<Duration> REQUEST_SIGNING_TIME = SdkMetric.of("RequestSigningTime", Duration.class, MetricCategory.DEFAULT);
20+
21+
/**
22+
* The duration of time between sending the HTTP request and when the HTTP response is received.
23+
*/
24+
public static final SdkMetric<Duration> REQUEST_EXECUTION_TIME = SdkMetric.of("RequestExecutionTime", Duration.class, MetricCategory.DEFAULT);
25+
26+
private DefaultMetrics() {
27+
}
28+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
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;
17+
18+
import software.amazon.awssdk.annotations.SdkPublicApi;
19+
20+
/**
21+
* A enum class representing the different types of metric categories in the SDK.
22+
* <p>
23+
* A metric can be tagged with multiple categories. Clients can enable/disable metric collection
24+
* at a {@link MetricCategory} level.
25+
*/
26+
@SdkPublicApi
27+
public enum MetricCategory {
28+
29+
/**
30+
* All metrics defined by the SDK are classified under this category at a minimum. If the metrics feature is enabled
31+
* but the category to collect is not, only metrics that are classified under this category are collected by the SDK
32+
*/
33+
DEFAULT("default"),
34+
35+
/**
36+
* Metrics collected at the http client level are classified under this category.
37+
*/
38+
HTTP_CLIENT("httpclient"),
39+
40+
/**
41+
* Metrics specific to streaming, eventStream APIs are classified under this category.
42+
*/
43+
STREAMING("streaming"),
44+
45+
/**
46+
* This is an umbrella category (provided for convenience) that records metrics belonging to every category
47+
* defined in this enum. Clients who wish to collect lot of SDK metrics data should use this.
48+
* <p>
49+
* Note: Enabling this option is verbose and can be expensive based on the platform the metrics are uploaded to.
50+
* Please make sure you need all this data before using this category.
51+
*/
52+
ALL("all")
53+
54+
;
55+
56+
private final String value;
57+
58+
MetricCategory(String value) {
59+
this.value = value;
60+
}
61+
62+
public String getValue() {
63+
return value;
64+
}
65+
66+
/**
67+
* Create a {@link MetricCategory} from the given String value. This method is case insensitive.
68+
*
69+
* @param value the value to create the {@link MetricCategory} from
70+
* @return A {@link MetricCategory} if the given {@link #value} matches one of the enum values.
71+
* Otherwise throws {@link IllegalArgumentException}
72+
*/
73+
public static MetricCategory fromString(String value) {
74+
for (MetricCategory mc : MetricCategory.values()) {
75+
if (mc.value.equalsIgnoreCase(value)) {
76+
return mc;
77+
}
78+
}
79+
80+
throw new IllegalArgumentException("MetricCategory cannot be created from value: " + value);
81+
}
82+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package software.amazon.awssdk.metrics;
2+
3+
import java.util.Collection;
4+
import java.util.List;
5+
import java.util.Optional;
6+
7+
/**
8+
* An immutable collection of metrics.
9+
*/
10+
public interface MetricCollection extends Iterable<MetricRecord<?>> {
11+
/**
12+
* @return The name of this metric collection.
13+
*/
14+
String name();
15+
16+
/**
17+
* Return all the values of the given metric.
18+
*
19+
* @param metric The metric.
20+
* @param <T> The type of the value.
21+
* @return All of the values of this metric.
22+
*/
23+
<T> List<T> getAllMetricValues(SdkMetric<T> metric);
24+
25+
/**
26+
* Return the values of the given metric.
27+
*
28+
* @param metric The metric.
29+
* @param <T> The type of the value.
30+
* @return The value of this metric.
31+
*/
32+
<T> Optional<T> getMetricValue(SdkMetric<T> metric);
33+
34+
/**
35+
* @return The child metric collections.
36+
*/
37+
Collection<MetricCollection> children();
38+
}

0 commit comments

Comments
 (0)