Skip to content

Commit 7f3da3f

Browse files
authored
Adds support for tracking feature usage in a new user agent metadata section (aws#5667)
* Adds support for tracking feature usage in a new user agent metadata section and adds a base set of features. Where features were already a part of the user agent string, they are now converted to the new format where a feature is represented as a Base64 encoded string, listed in BusinessMetricsFeatureId. For example, using DynamoDb Enhanced Client was previously recorded as 'hll/ddb-enh' in the user agent, but is now a 'd' in the business metrics metadata section 'm/'. * Minor changes from PR feedback * More module separation fixes * fix corrupted test file * Changing class name * removing this test to include in separate PR
1 parent 0cda47e commit 7f3da3f

File tree

32 files changed

+700
-152
lines changed

32 files changed

+700
-152
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Adds support for tracking feature usage in a new user agent metadata section and adds a base set of features. Where features were already a part of the user agent string, they are now converted to the new format where a feature is represented as a Base64 encoded string. For example, using DynamoDb Enhanced Client was previously recorded as 'hll/ddb-enh' in the user agent, but is now a 'd' in the business metrics metadata section 'm/'."
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/common/UserAgentUtilsSpec.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,10 @@
3030
import software.amazon.awssdk.codegen.poet.PoetExtension;
3131
import software.amazon.awssdk.codegen.poet.PoetUtils;
3232
import software.amazon.awssdk.core.ApiName;
33-
import software.amazon.awssdk.core.util.VersionInfo;
33+
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
3434

3535
public class UserAgentUtilsSpec implements ClassSpec {
3636

37-
private static final String PAGINATOR_USER_AGENT = "PAGINATED";
38-
3937
protected final IntermediateModel model;
4038
protected final PoetExtension poetExtensions;
4139

@@ -106,12 +104,12 @@ private MethodSpec applyPaginatorUserAgentMethod() {
106104
.addParameter(typeVariableName, "request")
107105
.addTypeVariable(typeVariableName)
108106
.addStatement("return applyUserAgentInfo(request, b -> b.addApiName($T.builder()"
109-
+ ".version($T.SDK_VERSION)"
110107
+ ".name($S)"
108+
+ ".version($S)"
111109
+ ".build()))",
112110
ApiName.class,
113-
VersionInfo.class,
114-
PAGINATOR_USER_AGENT)
111+
"sdk-metrics",
112+
BusinessMetricFeatureId.PAGINATOR)
115113
.returns(typeVariableName)
116114
.build();
117115
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/waiters/BaseWaiterClassSpec.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import software.amazon.awssdk.codegen.poet.PoetUtils;
5858
import software.amazon.awssdk.core.ApiName;
5959
import software.amazon.awssdk.core.internal.waiters.WaiterAttribute;
60+
import software.amazon.awssdk.core.useragent.BusinessMetricFeatureId;
6061
import software.amazon.awssdk.core.waiters.WaiterAcceptor;
6162
import software.amazon.awssdk.core.waiters.WaiterOverrideConfiguration;
6263
import software.amazon.awssdk.core.waiters.WaiterState;
@@ -75,7 +76,7 @@ public abstract class BaseWaiterClassSpec implements ClassSpec {
7576
public static final String FAILURE_MESSAGE_FORMAT_FOR_ERROR_MATCHER = "A waiter acceptor was matched on error "
7677
+ "condition (%s) and transitioned the waiter to "
7778
+ "failure state";
78-
private static final String WAITERS_USER_AGENT = "waiter";
79+
private static final String WAITERS_USER_AGENT = "B";
7980
private final IntermediateModel model;
8081
private final String modelPackage;
8182
private final Map<String, WaiterDefinition> waiters;
@@ -403,11 +404,11 @@ static MethodSpec applyWaitersUserAgentMethod(PoetExtension poetExtensions, Inte
403404
.get(ClassName.get(Consumer.class), ClassName.get(AwsRequestOverrideConfiguration.Builder.class));
404405

405406
CodeBlock codeBlock = CodeBlock.builder()
406-
.addStatement("$T userAgentApplier = b -> b.addApiName($T.builder().version"
407-
+ "($S).name($S).build())",
407+
.addStatement("$T userAgentApplier = b -> b.addApiName($T.builder().name"
408+
+ "($S).version($S).build())",
408409
parameterizedTypeName, ApiName.class,
409-
WAITERS_USER_AGENT,
410-
"hll")
410+
"sdk-metrics",
411+
BusinessMetricFeatureId.WAITER)
411412
.addStatement("$T overrideConfiguration =\n"
412413
+ " request.overrideConfiguration().map(c -> c.toBuilder()"
413414
+ ".applyMutation"

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/common/test-user-agent-class.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import software.amazon.awssdk.annotations.SdkInternalApi;
66
import software.amazon.awssdk.awscore.AwsRequestOverrideConfiguration;
77
import software.amazon.awssdk.core.ApiName;
8-
import software.amazon.awssdk.core.util.VersionInfo;
98
import software.amazon.awssdk.services.json.model.JsonRequest;
109

1110
@Generated("software.amazon.awssdk:codegen")
@@ -23,7 +22,6 @@ public static <T extends JsonRequest> T applyUserAgentInfo(T request,
2322
}
2423

2524
public static <T extends JsonRequest> T applyPaginatorUserAgent(T request) {
26-
return applyUserAgentInfo(request,
27-
b -> b.addApiName(ApiName.builder().version(VersionInfo.SDK_VERSION).name("PAGINATED").build()));
25+
return applyUserAgentInfo(request, b -> b.addApiName(ApiName.builder().name("sdk-metrics").version("C").build()));
2826
}
2927
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/waiters/query-async-waiter-class.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ public static QueryAsyncWaiter.Builder builder() {
125125

126126
private <T extends QueryRequest> T applyWaitersUserAgent(T request) {
127127
Consumer<AwsRequestOverrideConfiguration.Builder> userAgentApplier = b -> b.addApiName(ApiName.builder()
128-
.version("waiter").name("hll").build());
128+
.name("sdk-metrics").version("B").build());
129129
AwsRequestOverrideConfiguration overrideConfiguration = request.overrideConfiguration()
130130
.map(c -> c.toBuilder().applyMutation(userAgentApplier).build())
131131
.orElse((AwsRequestOverrideConfiguration.builder().applyMutation(userAgentApplier).build()));

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/waiters/query-sync-waiter-class.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public static QueryWaiter.Builder builder() {
107107

108108
private <T extends QueryRequest> T applyWaitersUserAgent(T request) {
109109
Consumer<AwsRequestOverrideConfiguration.Builder> userAgentApplier = b -> b.addApiName(ApiName.builder()
110-
.version("waiter").name("hll").build());
110+
.name("sdk-metrics").version("B").build());
111111
AwsRequestOverrideConfiguration overrideConfiguration = request.overrideConfiguration()
112112
.map(c -> c.toBuilder().applyMutation(userAgentApplier).build())
113113
.orElse((AwsRequestOverrideConfiguration.builder().applyMutation(userAgentApplier).build()));

core/aws-core/src/main/java/software/amazon/awssdk/awscore/internal/AwsExecutionContextBuilder.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616
package software.amazon.awssdk.awscore.internal;
1717

1818
import static software.amazon.awssdk.auth.signer.internal.util.SignerMethodResolver.resolveSigningMethodUsed;
19+
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY;
20+
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY;
1921
import static software.amazon.awssdk.core.interceptor.SdkExecutionAttribute.RESOLVED_CHECKSUM_SPECS;
22+
import static software.amazon.awssdk.core.internal.useragent.BusinessMetricsUtils.resolveRetryMode;
2023

2124
import java.util.Map;
25+
import java.util.Optional;
2226
import software.amazon.awssdk.annotations.SdkInternalApi;
2327
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute;
2428
import software.amazon.awssdk.awscore.AwsExecutionAttribute;
@@ -45,6 +49,7 @@
4549
import software.amazon.awssdk.core.internal.InternalCoreExecutionAttribute;
4650
import software.amazon.awssdk.core.internal.util.HttpChecksumResolver;
4751
import software.amazon.awssdk.core.signer.Signer;
52+
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
4853
import software.amazon.awssdk.endpoints.EndpointProvider;
4954
import software.amazon.awssdk.http.auth.scheme.NoAuthAuthScheme;
5055
import software.amazon.awssdk.http.auth.spi.scheme.AuthScheme;
@@ -116,7 +121,8 @@ private AwsExecutionContextBuilder() {
116121
clientConfig.option(AwsClientOption.USE_GLOBAL_ENDPOINT))
117122
.putAttribute(AwsExecutionAttribute.AWS_AUTH_ACCOUNT_ID_ENDPOINT_MODE,
118123
clientConfig.option(AwsClientOption.ACCOUNT_ID_ENDPOINT_MODE))
119-
.putAttribute(RESOLVED_CHECKSUM_SPECS, HttpChecksumResolver.resolveChecksumSpecs(executionAttributes));
124+
.putAttribute(RESOLVED_CHECKSUM_SPECS, HttpChecksumResolver.resolveChecksumSpecs(executionAttributes))
125+
.putAttribute(SdkInternalExecutionAttribute.BUSINESS_METRICS, resolveUserAgentBusinessMetrics(clientConfig));
120126

121127
// Auth Scheme resolution related attributes
122128
putAuthSchemeResolutionAttributes(executionAttributes, clientConfig, originalRequest);
@@ -264,7 +270,6 @@ private static MetricCollector resolveMetricCollector(ClientExecutionParams<?, ?
264270
return metricCollector;
265271
}
266272

267-
268273
/**
269274
* Resolves the endpoint provider, with the request override configuration taking precedence over the
270275
* provided default client clientConfig.
@@ -277,5 +282,11 @@ private static EndpointProvider resolveEndpointProvider(SdkRequest request,
277282
.orElse(clientConfig.option(SdkClientOption.ENDPOINT_PROVIDER));
278283
}
279284

280-
285+
private static BusinessMetricCollection resolveUserAgentBusinessMetrics(SdkClientConfiguration clientConfig) {
286+
BusinessMetricCollection businessMetrics = new BusinessMetricCollection();
287+
Optional<String> retryModeMetric = resolveRetryMode(clientConfig.option(RETRY_POLICY),
288+
clientConfig.option(RETRY_STRATEGY));
289+
retryModeMetric.ifPresent(businessMetrics::addMetric);
290+
return businessMetrics;
291+
}
281292
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE;
4545
import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_FILE_SUPPLIER;
4646
import static software.amazon.awssdk.core.client.config.SdkClientOption.PROFILE_NAME;
47-
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_POLICY;
4847
import static software.amazon.awssdk.core.client.config.SdkClientOption.RETRY_STRATEGY;
4948
import static software.amazon.awssdk.core.client.config.SdkClientOption.SCHEDULED_EXECUTOR_SERVICE;
5049
import static software.amazon.awssdk.core.client.config.SdkClientOption.SYNC_HTTP_CLIENT;
@@ -53,7 +52,6 @@
5352
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.HTTP;
5453
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.INTERNAL_METADATA_MARKER;
5554
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.IO;
56-
import static software.amazon.awssdk.core.internal.useragent.UserAgentConstant.RETRY_MODE;
5755
import static software.amazon.awssdk.utils.CollectionUtils.mergeLists;
5856
import static software.amazon.awssdk.utils.Validate.paramNotNull;
5957

@@ -96,7 +94,6 @@
9694
import software.amazon.awssdk.core.internal.useragent.SdkClientUserAgentProperties;
9795
import software.amazon.awssdk.core.internal.useragent.SdkUserAgentBuilder;
9896
import software.amazon.awssdk.core.retry.RetryMode;
99-
import software.amazon.awssdk.core.retry.RetryPolicy;
10097
import software.amazon.awssdk.core.util.SystemUserAgent;
10198
import software.amazon.awssdk.http.ExecutableHttpRequest;
10299
import software.amazon.awssdk.http.HttpExecuteRequest;
@@ -108,9 +105,6 @@
108105
import software.amazon.awssdk.profiles.ProfileFile;
109106
import software.amazon.awssdk.profiles.ProfileFileSystemSetting;
110107
import software.amazon.awssdk.profiles.ProfileProperty;
111-
import software.amazon.awssdk.retries.AdaptiveRetryStrategy;
112-
import software.amazon.awssdk.retries.LegacyRetryStrategy;
113-
import software.amazon.awssdk.retries.StandardRetryStrategy;
114108
import software.amazon.awssdk.retries.api.RetryStrategy;
115109
import software.amazon.awssdk.utils.AttributeMap;
116110
import software.amazon.awssdk.utils.AttributeMap.LazyValueSource;
@@ -394,31 +388,12 @@ protected SdkClientConfiguration invokePlugins(SdkClientConfiguration config) {
394388
return config;
395389
}
396390

397-
//TODO (useragent): Refactor this as part of moving value to business metrics (UA 2.1)
398-
private static String resolveRetryMode(RetryPolicy retryPolicy, RetryStrategy retryStrategy) {
399-
if (retryPolicy != null) {
400-
return retryPolicy.retryMode().toString();
401-
}
402-
if (retryStrategy instanceof StandardRetryStrategy) {
403-
return RetryMode.STANDARD.toString();
404-
}
405-
if (retryStrategy instanceof LegacyRetryStrategy) {
406-
return RetryMode.LEGACY.toString();
407-
}
408-
if (retryStrategy instanceof AdaptiveRetryStrategy) {
409-
return RetryMode.ADAPTIVE.toString();
410-
}
411-
return "UnknownRetryMode";
412-
}
413-
414391
private String resolveClientUserAgent(LazyValueSource config) {
415392
SdkClientUserAgentProperties clientProperties = new SdkClientUserAgentProperties();
416393

417394
ClientType clientType = config.get(CLIENT_TYPE);
418395
ClientType resolvedClientType = clientType == null ? ClientType.UNKNOWN : clientType;
419396

420-
clientProperties.putProperty(RETRY_MODE, StringUtils.lowerCase(resolveRetryMode(config.get(RETRY_POLICY),
421-
config.get(RETRY_STRATEGY))));
422397
clientProperties.putProperty(INTERNAL_METADATA_MARKER, StringUtils.trimToEmpty(config.get(INTERNAL_USER_AGENT)));
423398
clientProperties.putProperty(IO, StringUtils.lowerCase(resolvedClientType.name()));
424399
clientProperties.putProperty(HTTP, SdkHttpUtils.urlEncode(clientName(resolvedClientType,

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import software.amazon.awssdk.core.internal.useragent.SdkClientUserAgentProperties;
3434
import software.amazon.awssdk.core.retry.RetryMode;
3535
import software.amazon.awssdk.core.retry.RetryPolicy;
36+
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
3637
import software.amazon.awssdk.endpoints.EndpointProvider;
3738
import software.amazon.awssdk.http.SdkHttpClient;
3839
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
@@ -272,6 +273,14 @@ public final class SdkClientOption<T> extends ClientOption<T> {
272273
*/
273274
public static final SdkClientOption<String> CLIENT_USER_AGENT = new SdkClientOption<>(String.class);
274275

276+
/**
277+
* A user agent prefix that is specific to the client (agnostic of the request).
278+
*
279+
* Not currently in use, since the introduction of {@link SdkClientUserAgentProperties}
280+
*/
281+
public static final SdkClientOption<BusinessMetricCollection> BUSINESS_METRICS =
282+
new SdkClientOption<>(BusinessMetricCollection.class);
283+
275284
/**
276285
* Option to specify the default retry mode.
277286
*

core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/SdkInternalExecutionAttribute.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import software.amazon.awssdk.core.interceptor.trait.HttpChecksum;
2727
import software.amazon.awssdk.core.interceptor.trait.HttpChecksumRequired;
2828
import software.amazon.awssdk.core.internal.interceptor.trait.RequestCompression;
29+
import software.amazon.awssdk.core.useragent.BusinessMetricCollection;
2930
import software.amazon.awssdk.endpoints.Endpoint;
3031
import software.amazon.awssdk.endpoints.EndpointProvider;
3132
import software.amazon.awssdk.http.SdkHttpExecutionAttributes;
@@ -46,6 +47,12 @@ public final class SdkInternalExecutionAttribute extends SdkExecutionAttribute {
4647
*/
4748
public static final ExecutionAttribute<Boolean> IS_FULL_DUPLEX = new ExecutionAttribute<>("IsFullDuplex");
4849

50+
/**
51+
* A collection of business metrics feature ids.
52+
*/
53+
public static final ExecutionAttribute<BusinessMetricCollection> BUSINESS_METRICS =
54+
new ExecutionAttribute<>("BusinessMetricsCollection");
55+
4956
/**
5057
* If true, indicates that this is an event streaming request being sent over RPC, and therefore the serialized
5158
* request object is encapsulated as an event of type {@code initial-request}.

0 commit comments

Comments
 (0)