@Override protected HttpResponse doReceiveResponse( final HttpRequest request, final HttpClientConnection conn, final HttpContext context) throws HttpException, IOException { AWSRequestMetrics awsRequestMetrics = (AWSRequestMetrics) context .getAttribute(AWSRequestMetrics.class.getSimpleName()); if (awsRequestMetrics == null) { return super.doReceiveResponse(request, conn, context); } awsRequestMetrics.startEvent(Field.HttpClientReceiveResponseTime); try { return super.doReceiveResponse(request, conn, context); } finally { awsRequestMetrics.endEvent(Field.HttpClientReceiveResponseTime); } }
private void execRequest(String endpoint, int status) { AWSRequestMetrics metrics = new AWSRequestMetricsFullSupport(); metrics.addProperty(AWSRequestMetrics.Field.ServiceName, "AmazonCloudWatch"); metrics.addProperty(AWSRequestMetrics.Field.ServiceEndpoint, endpoint); metrics.addProperty(AWSRequestMetrics.Field.StatusCode, "" + status); if (status == 503) { metrics.addProperty(AWSRequestMetrics.Field.AWSErrorCode, "Throttled"); } String counterName = "BytesProcessed"; String timerName = "ClientExecuteTime"; metrics.setCounter(counterName, 12345); metrics.getTimingInfo().addSubMeasurement(timerName, TimingInfo.unmodifiableTimingInfo(100000L, 200000L)); Request<?> req = new DefaultRequest(new ListMetricsRequest(), "AmazonCloudWatch"); req.setAWSRequestMetrics(metrics); req.setEndpoint(URI.create(endpoint)); HttpResponse hr = new HttpResponse(req, new HttpPost(endpoint)); hr.setStatusCode(status); Response<?> resp = new Response<>(null, new HttpResponse(req, new HttpPost(endpoint))); collector.collectMetrics(req, resp); }
private <T> T invoke(HttpMethodName httpMethodName, String resourcePath, Object representation, HttpResponseHandler<AmazonWebServiceResponse<T>> responseHandler) throws AmazonClientException { ExecutionContext executionContext = createExecutionContext(); AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.RequestMarshallTime.name()); Request request = buildRequest(httpMethodName, resourcePath, representation); awsRequestMetrics.endEvent(AWSRequestMetrics.Field.RequestMarshallTime.name()); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.CredentialsRequestTime.name()); AWSCredentials credentials = awsCredentialsProvider.getCredentials(); awsRequestMetrics.endEvent(AWSRequestMetrics.Field.CredentialsRequestTime.name()); executionContext.setCredentials(credentials); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.ClientExecuteTime.name()); Response<T> response = client.execute(request, responseHandler, errorResponseHandler, executionContext); awsRequestMetrics.endEvent(AWSRequestMetrics.Field.ClientExecuteTime.name()); awsRequestMetrics.log(); return response.getAwsResponse(); }
/** * Convenient method to end the client execution without logging the * awsRequestMetrics. */ protected final void endClientExecution( AWSRequestMetrics awsRequestMetrics, Request<?> request, Response<?> response) { this.endClientExecution(awsRequestMetrics, request, response, !LOGGING_AWS_REQUEST_METRIC); }
/** * Common routine to end a client AWS request/response execution and collect * the request metrics. Caller of this routine is responsible for starting * the event for {@link Field#ClientExecuteTime} and call this method * in a try-finally block. * * @param loggingAwsRequestMetrics deprecated and ignored */ protected final void endClientExecution( AWSRequestMetrics awsRequestMetrics, Request<?> request, Response<?> response, @Deprecated boolean loggingAwsRequestMetrics) { if (request != null) { awsRequestMetrics.endEvent(Field.ClientExecuteTime); awsRequestMetrics.getTimingInfo().endTiming(); RequestMetricCollector c = findRequestMetricCollector( request.getOriginalRequest().getRequestMetricCollector()); c.collectMetrics(request, response); awsRequestMetrics.log(); } }
@Override protected HttpResponse doSendRequest( final HttpRequest request, final HttpClientConnection conn, final HttpContext context) throws IOException, HttpException { AWSRequestMetrics awsRequestMetrics = (AWSRequestMetrics) context .getAttribute(AWSRequestMetrics.class.getSimpleName()); if (awsRequestMetrics == null) { return super.doSendRequest(request, conn, context); } if (conn instanceof ManagedHttpClientConnection) { ManagedHttpClientConnection managedConn = (ManagedHttpClientConnection)conn; Socket sock = managedConn.getSocket(); if (sock instanceof SdkMetricsSocket) { SdkMetricsSocket sdkMetricsSocket = (SdkMetricsSocket)sock; sdkMetricsSocket.setMetrics(awsRequestMetrics); } else if (sock instanceof SdkSSLMetricsSocket) { SdkSSLMetricsSocket sdkSSLMetricsSocket = (SdkSSLMetricsSocket)sock; sdkSSLMetricsSocket.setMetrics(awsRequestMetrics); } } awsRequestMetrics.startEvent(Field.HttpClientSendRequestTime); try { return super.doSendRequest(request, conn, context); } finally { awsRequestMetrics.endEvent(Field.HttpClientSendRequestTime); } }
/** * @param delegate Response handler to delegate to and unwrap * @param request Marshalled request * @param awsRequestMetrics Request metrics */ public AwsResponseHandlerAdapter(HttpResponseHandler<AmazonWebServiceResponse<T>> delegate, Request<?> request, AWSRequestMetrics awsRequestMetrics, MetadataCache responseMetadataCache) { this.delegate = delegate; this.request = request; this.awsRequestMetrics = awsRequestMetrics; this.responseMetadataCache = responseMetadataCache; }
@Override public T handle(HttpResponse response) throws Exception { final AmazonWebServiceResponse<T> awsResponse = delegate.handle(response); if (awsResponse == null) { throw new RuntimeException("Unable to unmarshall response metadata. Response Code: " + response.getStatusCode() + ", Response Text: " + response.getStatusText()); } AmazonWebServiceRequest userRequest = request.getOriginalRequest(); if (userRequest.getCloneRoot() != null) { userRequest = userRequest.getCloneRoot(); } responseMetadataCache.add(userRequest, awsResponse.getResponseMetadata()); final String awsRequestId = awsResponse.getRequestId(); if (requestLog.isDebugEnabled()) { requestLog .debug("Received successful response: " + response.getStatusCode() + ", AWS Request ID: " + awsRequestId); } if (!logHeaderRequestId(response)) { // Logs the AWS request ID extracted from the payload if // it is not available from the response header. logResponseRequestId(awsRequestId); } awsRequestMetrics.addProperty(AWSRequestMetrics.Field.AWSRequestID, awsRequestId); return fillInResponseMetadata(awsResponse, response); }
@Deprecated public ExecutionContext(List<RequestHandler2> requestHandler2s, boolean isMetricEnabled, AmazonWebServiceClient awsClient) { this.requestHandler2s = requestHandler2s; awsRequestMetrics = isMetricEnabled ? new AWSRequestMetricsFullSupport() : new AWSRequestMetrics(); this.awsClient = awsClient; this.signerProvider = new SignerProvider() { @Override public Signer getSigner(SignerProviderContext context) { return getSignerByURI(context.getUri()); } }; }
@Override public boolean retryRequest( final IOException exception, int executionCount, final HttpContext context) { boolean retry = super.retryRequest(exception, executionCount, context); if (retry) { AWSRequestMetrics awsRequestMetrics = (AWSRequestMetrics) context .getAttribute(AWSRequestMetrics.class.getSimpleName()); if (awsRequestMetrics != null) { awsRequestMetrics.incrementCounter(Field.HttpClientRetryCount); } } return retry; }
@Override public AmazonServiceException handle(HttpResponse response) throws Exception { final AmazonServiceException ase = handleAse(response); ase.setStatusCode(response.getStatusCode()); ase.setServiceName(response.getRequest().getServiceName()); awsRequestMetrics.addPropertyWith(AWSRequestMetrics.Field.AWSRequestID, ase.getRequestId()) .addPropertyWith(AWSRequestMetrics.Field.AWSErrorCode, ase.getErrorCode()) .addPropertyWith(AWSRequestMetrics.Field.StatusCode, ase.getStatusCode()); return ase; }
@Override public void setAWSRequestMetrics(AWSRequestMetrics metrics) { if (this.metrics == null) { this.metrics = metrics; } else { throw new IllegalStateException("AWSRequestMetrics has already been set on this request"); } }
@Override public <Input, Output> Output execute( ClientExecutionParams<Input, Output> executionParams) { final Input input = executionParams.getInput(); ExecutionContext executionContext = createExecutionContext( executionParams.getRequestConfig()); AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics(); awsRequestMetrics.startEvent(AWSRequestMetrics.Field.ClientExecuteTime); Request<Input> request = null; Response<Output> response = null; try { awsRequestMetrics.startEvent(AWSRequestMetrics.Field.RequestMarshallTime); try { request = executionParams.getMarshaller().marshall(input); request.setAWSRequestMetrics(awsRequestMetrics); } finally { awsRequestMetrics.endEvent(AWSRequestMetrics.Field.RequestMarshallTime); } response = invoke(request, executionParams.getRequestConfig(), executionContext, executionParams.getResponseHandler(), executionParams.getErrorResponseHandler()); return response.getAwsResponse(); } finally { endClientExecution(awsRequestMetrics, executionParams.getRequestConfig(), request, response); } }
/** * Convenient method to end the client execution without logging the awsRequestMetrics. */ private void endClientExecution(AWSRequestMetrics awsRequestMetrics, RequestConfig requestConfig, Request<?> request, Response<?> response) { if (request != null) { awsRequestMetrics.endEvent(AWSRequestMetrics.Field.ClientExecuteTime); awsRequestMetrics.getTimingInfo().endTiming(); RequestMetricCollector metricCollector = findRequestMetricCollector(requestConfig); metricCollector.collectMetrics(request, response); awsRequestMetrics.log(); } }
/** * {@link AWSRequestMetrics} is set per request. */ public void setMetrics(AWSRequestMetrics metrics) { if (metricsIS == null) { throw new IllegalStateException( "The underlying input stream must be initialized!"); } metricsIS.setMetrics(metrics); }
@SuppressWarnings("deprecation") @Override public void afterResponse(Request<?> request, Response<?> response) { AWSRequestMetrics awsRequestMetrics = request == null ? null : request .getAWSRequestMetrics(); Object awsResponse = response == null ? null : response .getAwsResponse(); TimingInfo timingInfo = awsRequestMetrics == null ? null : awsRequestMetrics.getTimingInfo(); old.afterResponse(request, awsResponse, timingInfo); }
@Test public void testSetMetrics() throws IOException { SSLSocket sock = mock(SSLSocket.class); InputStream inputStream = mock(InputStream.class); when(sock.getInputStream()).thenReturn(inputStream); SdkSSLMetricsSocket sdkSSLMetricsSocket = new SdkSSLMetricsSocket(sock); sdkSSLMetricsSocket.setMetrics(new AWSRequestMetrics()); Assert.assertThat(sdkSSLMetricsSocket.getMetricsInputStream(), not(nullValue())); }
@Override public void collectMetrics(Request<?> request, Response<?> response) { AWSRequestMetrics metrics = request.getAWSRequestMetrics(); TimingInfo timingInfo = metrics.getTimingInfo(); Number requestCounts = timingInfo.getCounter(Field.RequestCount.name()); Number retryCounts = timingInfo.getCounter(Field.HttpClientRetryCount.name()); Number throttleExceptions = timingInfo.getCounter(Field.ThrottleException.name()); TimingInfo requestTime = timingInfo.getSubMeasurement(Field.HttpRequestTime.name()); if (requestCounts != null) { stats.updateAwsRequestCount(requestCounts.longValue()); } if (retryCounts != null) { stats.updateAwsRetryCount(retryCounts.longValue()); } if (throttleExceptions != null) { stats.updateAwsThrottleExceptionsCount(throttleExceptions.longValue()); } if (requestTime != null && requestTime.getTimeTakenMillisIfKnown() != null) { stats.addAwsRequestTime(new Duration(requestTime.getTimeTakenMillisIfKnown(), MILLISECONDS)); } }
@Override public void collectMetrics(Request<?> request, Response<?> response) { final AWSRequestMetrics metrics = request.getAWSRequestMetrics(); if (metrics.isEnabled()) { final Map<String, String> baseTags = getBaseTags(request); final TimingInfo timing = metrics.getTimingInfo(); for (Field counter : COUNTERS) { Optional.ofNullable(timing.getCounter(counter.name())) .filter(v -> v.longValue() > 0) .ifPresent(v -> registry.counter(metricId(counter, baseTags)).increment(v.longValue())); } for (Field timer : TIMERS) { Optional.ofNullable(timing.getLastSubMeasurement(timer.name())) .filter(TimingInfo::isEndTimeKnown) .ifPresent(t -> registry.timer(metricId(timer, baseTags)) .record(t.getEndTimeNano() - t.getStartTimeNano(), TimeUnit.NANOSECONDS)); } notEmpty(metrics.getProperty(Field.ThrottleException)).ifPresent(throttleExceptions -> { final Id throttling = metricId("throttling", baseTags); throttleExceptions.forEach(ex -> registry.counter(throttling.withTag("throttleException", ex.getClass().getSimpleName())) .increment()); }); } }
private static boolean isError(AWSRequestMetrics metrics) { for (TagField err : ERRORS) { if (err.getValue(metrics).isPresent()) { return true; } } return false; }
private ExecutionContext(final Builder builder) { this.requestHandler2s = builder.requestHandler2s; this.awsRequestMetrics = builder.useRequestMetrics ? new AWSRequestMetricsFullSupport() : new AWSRequestMetrics(); this.awsClient = builder.awsClient; this.signerProvider = builder.signerProvider; }
public AWSRequestMetrics getAwsRequestMetrics() { return awsRequestMetrics; }
AwsErrorResponseHandler(HttpResponseHandler<AmazonServiceException> errorResponseHandler, AWSRequestMetrics awsRequestMetrics) { this.delegate = errorResponseHandler; this.awsRequestMetrics = awsRequestMetrics; }
@Override public AWSRequestMetrics getAWSRequestMetrics() { return metrics; }
public void setMetrics(AWSRequestMetrics metrics) { this.metrics = metrics; }
public void setMetrics(AWSRequestMetrics metrics) throws IOException { // make sure metricsIS is initialized. getInputStream(); metricsIS.setMetrics(metrics); }
@Override public AWSRequestMetrics getAWSRequestMetrics() { return null; }
@Override public void setAWSRequestMetrics(AWSRequestMetrics metrics) { }
/** * Tests AmazonHttpClient's behavior upon simulated service exceptions when the * request payload is repeatable. */ @Test public void testServiceExceptionHandling() { int random500StatusCode = 500 + random.nextInt(100); String randomErrorCode = UUID.randomUUID().toString(); // A mock HttpClient that always returns the specified status and error code. injectMockHttpClient(testedClient, new ReturnServiceErrorHttpClient(random500StatusCode, randomErrorCode)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); Request<?> testedRepeatableRequest = getSampleRequestWithRepeatableContent(originalRequest); // It should keep retrying until it reaches the max retry limit and // throws the simulated ASE. AmazonServiceException expectedServiceException = null; try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonServiceException is expected."); } catch (AmazonServiceException ase) { // We should see the original service exception Assert.assertEquals(random500StatusCode, ase.getStatusCode()); Assert.assertEquals(randomErrorCode, ase.getErrorCode()); expectedServiceException = ase; } // Verifies that the correct information was passed into the RetryCondition and BackoffStrategy verifyExpectedContextData(retryCondition, originalRequest, expectedServiceException, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, originalRequest, expectedServiceException, EXPECTED_RETRY_COUNT); // We also want to check the RequestCount metric is correctly captured. Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated IOException during * executing the http request when the request payload is repeatable. */ @Test public void testIOExceptioinHandling() { // A mock HttpClient that always throws the specified IOException object IOException simulatedIOException = new IOException("fake IOException"); injectMockHttpClient(testedClient, new ThrowingExceptionHttpClient(simulatedIOException)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); Request<?> testedRepeatableRequest = getSampleRequestWithRepeatableContent(originalRequest); // It should keep retrying until it reaches the max retry limit and // throws the an ACE containing the simulated IOException. AmazonClientException expectedClientException = null; try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonClientException is expected."); } catch (AmazonClientException ace) { Assert.assertTrue(simulatedIOException == ace.getCause()); expectedClientException = ace; } // Verifies that the correct information was passed into the RetryCondition and BackoffStrategy verifyExpectedContextData(retryCondition, originalRequest, expectedClientException, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, originalRequest, expectedClientException, EXPECTED_RETRY_COUNT); // We also want to check the RequestCount metric is correctly captured. Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated service exceptions when the * request payload is not repeatable. */ @Test public void testServiceExceptionHandlingWithNonRepeatableRequestContent() { int random500StatusCode = 500 + random.nextInt(100); String randomErrorCode = UUID.randomUUID().toString(); // A mock HttpClient that always returns the specified status and error code. injectMockHttpClient(testedClient, new ReturnServiceErrorHttpClient(random500StatusCode, randomErrorCode)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); // A non-repeatable request Request<?> testedNonRepeatableRequest = getSampleRequestWithNonRepeatableContent(originalRequest); // It should fail directly and throw the ASE, without consulting the // custom shouldRetry(..) method. try { testedClient.requestExecutionBuilder() .request(testedNonRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonServiceException is expected."); } catch (AmazonServiceException ase) { Assert.assertEquals(random500StatusCode, ase.getStatusCode()); Assert.assertEquals(randomErrorCode, ase.getErrorCode()); } // Verifies that shouldRetry and calculateSleepTime were never called verifyExpectedContextData(retryCondition, null, null, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, null, null, EXPECTED_RETRY_COUNT); Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated IOException when the * request payload is not repeatable. */ @Test public void testIOExceptionHandlingWithNonRepeatableRequestContent() { // A mock HttpClient that always throws the specified IOException object IOException simulatedIOException = new IOException("fake IOException"); injectMockHttpClient(testedClient, new ThrowingExceptionHttpClient(simulatedIOException)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); // A non-repeatable request Request<?> testedRepeatableRequest = getSampleRequestWithNonRepeatableContent(originalRequest); // It should fail directly and throw an ACE containing the simulated // IOException, without consulting the // custom shouldRetry(..) method. try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonClientException is expected."); } catch (AmazonClientException ace) { Assert.assertTrue(simulatedIOException == ace.getCause()); } // Verifies that shouldRetry and calculateSleepTime are still called verifyExpectedContextData(retryCondition, null, null, EXPECTED_RETRY_COUNT); verifyExpectedContextData(backoffStrategy, null, null, EXPECTED_RETRY_COUNT); Assert.assertEquals( EXPECTED_RETRY_COUNT + 1, // request count = retries + 1 context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
/** * Tests AmazonHttpClient's behavior upon simulated RuntimeException (which * should be handled as an unexpected failure and not retried). */ @Test public void testUnexpectedFailureHandling() { // A mock HttpClient that always throws an NPE NullPointerException simulatedNPE = new NullPointerException("fake NullPointerException"); injectMockHttpClient(testedClient, new ThrowingExceptionHttpClient(simulatedNPE)); // The ExecutionContext should collect the expected RequestCount ExecutionContext context = new ExecutionContext(true); Request<?> testedRepeatableRequest = getSampleRequestWithRepeatableContent(originalRequest); // It should fail directly and throw the simulated NPE, without // consulting the custom shouldRetry(..) method. try { testedClient.requestExecutionBuilder() .request(testedRepeatableRequest) .errorResponseHandler(errorResponseHandler) .executionContext(context) .execute(); Assert.fail("AmazonClientException is expected."); } catch (NullPointerException npe) { Assert.assertTrue(simulatedNPE == npe); } // Verifies that shouldRetry and calculateSleepTime were never called verifyExpectedContextData(retryCondition, null, null, 0); verifyExpectedContextData(backoffStrategy, null, null, 0); // The captured RequestCount should be 1 Assert.assertEquals( 1, context.getAwsRequestMetrics() .getTimingInfo().getCounter(AWSRequestMetrics.Field.RequestCount.toString()).intValue()); }
public Optional<String> getValue(AWSRequestMetrics metrics) { return firstValue(metrics.getProperty(field), tagExtractor); }
/** * Returns the request metrics. */ AWSRequestMetrics getAWSRequestMetrics();
/** * Bind the request metrics to the request. Note metrics can be captured * before the request is created. * * @throws IllegalStateException if the binding has already occurred */ void setAWSRequestMetrics(AWSRequestMetrics metrics);