Skip to content

Commit 3d57c58

Browse files
committed
Add HttpClient collector to HTTP request
1 parent 83d46ca commit 3d57c58

File tree

7 files changed

+169
-7
lines changed

7 files changed

+169
-7
lines changed

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStage.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import software.amazon.awssdk.core.internal.http.pipeline.RequestPipeline;
4444
import software.amazon.awssdk.core.internal.http.timers.TimeoutTracker;
4545
import software.amazon.awssdk.core.internal.http.timers.TimerUtils;
46+
import software.amazon.awssdk.core.internal.util.MetricUtils;
4647
import software.amazon.awssdk.core.metrics.CoreMetric;
4748
import software.amazon.awssdk.http.SdkHttpFullRequest;
4849
import software.amazon.awssdk.http.SdkHttpMethod;
@@ -143,11 +144,14 @@ private CompletableFuture<Response<OutputT>> executeHttpRequest(SdkHttpFullReque
143144
// Set content length if it hasn't been set already.
144145
SdkHttpFullRequest requestWithContentLength = getRequestWithContentLength(request, requestProvider);
145146

147+
MetricCollector httpMetricCollector = MetricUtils.createHttpMetricsCollector(context);
148+
146149
AsyncExecuteRequest executeRequest = AsyncExecuteRequest.builder()
147150
.request(requestWithContentLength)
148151
.requestContentPublisher(requestProvider)
149152
.responseHandler(wrappedResponseHandler)
150153
.fullDuplex(isFullDuplex(context.executionAttributes()))
154+
.metricCollector(httpMetricCollector)
151155
.build();
152156

153157
CompletableFuture<Void> httpClientFuture = doExecuteHttpRequest(context, executeRequest);

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeHttpRequestStage.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,20 @@ public Pair<SdkHttpFullRequest, SdkHttpFullResponse> execute(SdkHttpFullRequest
5959
}
6060

6161
private HttpExecuteResponse executeHttpRequest(SdkHttpFullRequest request, RequestExecutionContext context) throws Exception {
62+
MetricCollector metricCollector = context.metricCollector();
63+
64+
MetricCollector httpMetricCollector = MetricUtils.createHttpMetricsCollector(context);
65+
6266
ExecutableHttpRequest requestCallable = sdkHttpClient
6367
.prepareRequest(HttpExecuteRequest.builder()
6468
.request(request)
69+
.metricCollector(httpMetricCollector)
6570
.contentStreamProvider(request.contentStreamProvider().orElse(null))
6671
.build());
6772

6873
context.apiCallTimeoutTracker().abortable(requestCallable);
6974
context.apiCallAttemptTimeoutTracker().abortable(requestCallable);
7075

71-
MetricCollector metricCollector = context.metricCollector();
72-
7376
Pair<HttpExecuteResponse, Duration> measuredExecute = MetricUtils.measureDurationUnsafe(requestCallable);
7477

7578
metricCollector.reportMetric(CoreMetric.HTTP_REQUEST_ROUND_TRIP_TIME, measuredExecute.right());

core/sdk-core/src/main/java/software/amazon/awssdk/core/internal/util/MetricUtils.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import software.amazon.awssdk.http.SdkHttpFullResponse;
3232
import software.amazon.awssdk.metrics.MetricCollector;
3333
import software.amazon.awssdk.metrics.MetricPublisher;
34+
import software.amazon.awssdk.metrics.NoOpMetricCollector;
3435
import software.amazon.awssdk.utils.OptionalUtils;
3536
import software.amazon.awssdk.utils.Pair;
3637

@@ -114,8 +115,18 @@ public static void collectHttpMetrics(MetricCollector metricCollector, SdkHttpFu
114115
}
115116

116117
public static MetricCollector createAttemptMetricsCollector(RequestExecutionContext context) {
117-
return context.executionContext()
118-
.metricCollector()
119-
.createChild("ApiCallAttempt");
118+
MetricCollector parentCollector = context.executionContext().metricCollector();
119+
if (parentCollector != null) {
120+
return parentCollector.createChild("ApiCallAttempt");
121+
}
122+
return NoOpMetricCollector.create();
123+
}
124+
125+
public static MetricCollector createHttpMetricsCollector(RequestExecutionContext context) {
126+
MetricCollector parentCollector = context.metricCollector();
127+
if (parentCollector != null) {
128+
return parentCollector.createChild("HttpClient");
129+
}
130+
return NoOpMetricCollector.create();
120131
}
121132
}

core/sdk-core/src/test/java/software/amazon/awssdk/core/internal/http/pipeline/stages/MakeAsyncHttpRequestStageTest.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515

1616
package software.amazon.awssdk.core.internal.http.pipeline.stages;
1717

18+
import static org.assertj.core.api.Assertions.assertThat;
1819
import static org.mockito.Matchers.any;
1920
import static org.mockito.Matchers.anyLong;
21+
import static org.mockito.Matchers.eq;
22+
import static org.mockito.Mockito.mock;
2023
import static org.mockito.Mockito.never;
2124
import static org.mockito.Mockito.times;
2225
import static org.mockito.Mockito.verify;
@@ -34,17 +37,23 @@
3437
import org.junit.Before;
3538
import org.junit.Test;
3639
import org.junit.runner.RunWith;
40+
import org.mockito.ArgumentCaptor;
3741
import org.mockito.Mock;
3842
import org.mockito.runners.MockitoJUnitRunner;
3943
import software.amazon.awssdk.core.client.config.SdkAdvancedAsyncClientOption;
4044
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
4145
import software.amazon.awssdk.core.http.ExecutionContext;
4246
import software.amazon.awssdk.core.http.NoopTestRequest;
47+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
4348
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
4449
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
4550
import software.amazon.awssdk.core.internal.http.timers.ClientExecutionAndRequestTimerTestUtils;
4651
import software.amazon.awssdk.core.internal.util.AsyncResponseHandlerTestUtils;
52+
import software.amazon.awssdk.http.SdkHttpFullRequest;
53+
import software.amazon.awssdk.http.SdkHttpMethod;
54+
import software.amazon.awssdk.http.async.AsyncExecuteRequest;
4755
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
56+
import software.amazon.awssdk.metrics.MetricCollector;
4857
import utils.ValidSdkObjects;
4958

5059
@RunWith(MockitoJUnitRunner.class)
@@ -91,6 +100,49 @@ public void apiCallAttemptTimeoutNotEnabled_shouldNotInvokeExecutor() throws Exc
91100
verify(timeoutExecutor, never()).schedule(any(Runnable.class), anyLong(), any(TimeUnit.class));
92101
}
93102

103+
@Test
104+
public void testExecute_contextContainsMetricCollector_addsChildToExecuteRequest() {
105+
stage = new MakeAsyncHttpRequestStage<>(
106+
combinedAsyncResponseHandler(AsyncResponseHandlerTestUtils.noOpResponseHandler(),
107+
AsyncResponseHandlerTestUtils.noOpResponseHandler()),
108+
clientDependencies(null));
109+
110+
SdkHttpFullRequest sdkHttpRequest = SdkHttpFullRequest.builder()
111+
.method(SdkHttpMethod.GET)
112+
.host("mybucket.s3.us-west-2.amazonaws.com")
113+
.protocol("https")
114+
.build();
115+
116+
MetricCollector mockCollector = mock(MetricCollector.class);
117+
MetricCollector childCollector = mock(MetricCollector.class);
118+
119+
when(mockCollector.createChild(any(String.class))).thenReturn(childCollector);
120+
121+
ExecutionContext executionContext = ExecutionContext.builder()
122+
.executionAttributes(new ExecutionAttributes())
123+
.build();
124+
125+
RequestExecutionContext context = RequestExecutionContext.builder()
126+
.originalRequest(ValidSdkObjects.sdkRequest())
127+
.executionContext(executionContext)
128+
.build();
129+
130+
context.metricCollector(mockCollector);
131+
132+
try {
133+
stage.execute(sdkHttpRequest, context);
134+
} catch (Exception e) {
135+
e.printStackTrace();
136+
// ignored, don't really care about successful execution of the stage in this case
137+
} finally {
138+
ArgumentCaptor<AsyncExecuteRequest> httpRequestCaptor = ArgumentCaptor.forClass(AsyncExecuteRequest.class);
139+
140+
verify(mockCollector).createChild(eq("HttpClient"));
141+
verify(sdkAsyncHttpClient).execute(httpRequestCaptor.capture());
142+
assertThat(httpRequestCaptor.getValue().metricCollector()).contains(childCollector);
143+
}
144+
}
145+
94146
private HttpClientDependencies clientDependencies(Duration timeout) {
95147
SdkClientConfiguration configuration = SdkClientConfiguration.builder()
96148
.option(SdkAdvancedAsyncClientOption.FUTURE_COMPLETION_EXECUTOR, Runnable::run)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.core.internal.http.pipeline.stages;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
import static org.mockito.Matchers.any;
20+
import static org.mockito.Matchers.eq;
21+
import static org.mockito.Mockito.mock;
22+
import static org.mockito.Mockito.verify;
23+
import static org.mockito.Mockito.when;
24+
import static software.amazon.awssdk.core.client.config.SdkClientOption.SYNC_HTTP_CLIENT;
25+
import java.io.IOException;
26+
import org.junit.Before;
27+
import org.junit.Test;
28+
import org.junit.runner.RunWith;
29+
import org.mockito.ArgumentCaptor;
30+
import org.mockito.Mock;
31+
import org.mockito.runners.MockitoJUnitRunner;
32+
import software.amazon.awssdk.core.client.config.SdkClientConfiguration;
33+
import software.amazon.awssdk.core.http.ExecutionContext;
34+
import software.amazon.awssdk.core.internal.http.HttpClientDependencies;
35+
import software.amazon.awssdk.core.internal.http.RequestExecutionContext;
36+
import software.amazon.awssdk.core.internal.http.timers.TimeoutTracker;
37+
import software.amazon.awssdk.http.HttpExecuteRequest;
38+
import software.amazon.awssdk.http.SdkHttpClient;
39+
import software.amazon.awssdk.http.SdkHttpFullRequest;
40+
import software.amazon.awssdk.http.SdkHttpMethod;
41+
import software.amazon.awssdk.metrics.MetricCollector;
42+
import utils.ValidSdkObjects;
43+
44+
@RunWith(MockitoJUnitRunner.class)
45+
public class MakeHttpRequestStageTest {
46+
47+
@Mock
48+
private SdkHttpClient mockClient;
49+
50+
private MakeHttpRequestStage stage;
51+
52+
@Before
53+
public void setup() throws IOException {
54+
SdkClientConfiguration config = SdkClientConfiguration.builder().option(SYNC_HTTP_CLIENT, mockClient).build();
55+
stage = new MakeHttpRequestStage(HttpClientDependencies.builder().clientConfiguration(config).build());
56+
}
57+
58+
@Test
59+
public void testExecute_contextContainsMetricCollector_addsChildToExecuteRequest() {
60+
SdkHttpFullRequest sdkRequest = SdkHttpFullRequest.builder()
61+
.method(SdkHttpMethod.GET)
62+
.host("mybucket.s3.us-west-2.amazonaws.com")
63+
.protocol("https")
64+
.build();
65+
66+
MetricCollector mockCollector = mock(MetricCollector.class);
67+
MetricCollector childCollector = mock(MetricCollector.class);
68+
69+
when(mockCollector.createChild(any(String.class))).thenReturn(childCollector);
70+
71+
ExecutionContext executionContext = ExecutionContext.builder().build();
72+
73+
RequestExecutionContext context = RequestExecutionContext.builder()
74+
.originalRequest(ValidSdkObjects.sdkRequest())
75+
.executionContext(executionContext)
76+
.build();
77+
78+
context.metricCollector(mockCollector);
79+
context.apiCallAttemptTimeoutTracker(mock(TimeoutTracker.class));
80+
context.apiCallTimeoutTracker(mock(TimeoutTracker.class));
81+
82+
try {
83+
stage.execute(sdkRequest, context);
84+
} catch (Exception e) {
85+
// ignored, don't really care about successful execution of the stage in this case
86+
} finally {
87+
ArgumentCaptor<HttpExecuteRequest> httpRequestCaptor = ArgumentCaptor.forClass(HttpExecuteRequest.class);
88+
89+
verify(mockCollector).createChild(eq("HttpClient"));
90+
verify(mockClient).prepareRequest(httpRequestCaptor.capture());
91+
assertThat(httpRequestCaptor.getValue().metricCollector()).contains(childCollector);
92+
}
93+
}
94+
}

test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/CoreMetricsTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,6 @@ public void testApiCall_operationSuccessful_addsMetrics() {
161161
MetricCollection attemptCollection = capturedCollection.children().get(0);
162162

163163
assertThat(attemptCollection.name()).isEqualTo("ApiCallAttempt");
164-
assertThat(attemptCollection.children()).isEmpty();
165164
assertThat(attemptCollection.metricValues(CoreMetric.HTTP_STATUS_CODE))
166165
.containsExactly(200);
167166
assertThat(attemptCollection.metricValues(CoreMetric.SIGNING_DURATION).get(0))

test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/metrics/async/BaseAsyncCoreMetricsTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@ public void apiCall_operationSuccessful_addsMetrics() {
6363
MetricCollection attemptCollection = capturedCollection.children().get(0);
6464

6565
assertThat(attemptCollection.name()).isEqualTo("ApiCallAttempt");
66-
assertThat(attemptCollection.children()).isEmpty();
6766

6867
verifySuccessfulApiCallAttemptCollection(attemptCollection);
6968
assertThat(attemptCollection.metricValues(CoreMetric.HTTP_REQUEST_ROUND_TRIP_TIME).get(0))

0 commit comments

Comments
 (0)