/** * @return A new {@link AsyncRestTemplate} instance with a {@link WingtipsAsyncClientHttpRequestInterceptor} * already added and configured to surround downstream calls with a subspan, and using the default * {@link HttpTagAndSpanNamingStrategy} and {@link HttpTagAndSpanNamingAdapter} ({@link ZipkinHttpTagStrategy} and * {@link SpringHttpClientTagAdapter}). */ public static AsyncRestTemplate createTracingEnabledAsyncRestTemplate() { return createTracingEnabledAsyncRestTemplate(true); }
/** * @return A new {@link RestTemplate} instance with a {@link WingtipsClientHttpRequestInterceptor} already added * and configured to surround downstream calls with a subspan, and using the default * {@link HttpTagAndSpanNamingStrategy} and {@link HttpTagAndSpanNamingAdapter} ({@link ZipkinHttpTagStrategy} and * {@link SpringHttpClientTagAdapter}). */ public static RestTemplate createTracingEnabledRestTemplate() { return createTracingEnabledRestTemplate(true); }
@GetMapping(path = NESTED_ASYNC_CALL_PATH) @SuppressWarnings("unused") public DeferredResult<EndpointSpanInfoDto> getNestedAsyncCall() { DeferredResult<EndpointSpanInfoDto> asyncResponse = new DeferredResult<>(); executor.execute(runnableWithTracing(() -> { try { logger.info("Nested async call endpoint hit. Sleeping..."); sleepThread(SLEEP_TIME_MILLIS); URI nestedCallUri = URI.create( "http://localhost:" + serverPort + SPAN_INFO_CALL_PATH + "?someQuery=foobar" ); logger.info("...Calling: " + nestedCallUri.toString()); ListenableFuture<ResponseEntity<EndpointSpanInfoDto>> asyncRestTemplateResultFuture = wingtipsEnabledAsyncRestTemplate.exchange( nestedCallUri, HttpMethod.GET, getHttpEntityWithUserIdHeader(), EndpointSpanInfoDto.class ); asyncRestTemplateResultFuture.addCallback( successCallbackWithTracing(result -> { logger.info("AsyncRestTemplate call complete"); asyncResponse.setResult(result.getBody()); }), failureCallbackWithTracing(asyncResponse::setErrorResult) ); } catch(Throwable t) { asyncResponse.setErrorResult(t); } })); return asyncResponse; }
/** * Calls {@link WingtipsSpringUtil#propagateTracingHeaders(HttpMessage, Span)} to propagate the current span's * tracing state on the given request's headers, then returns * {@link AsyncClientHttpRequestExecution#executeAsync(HttpRequest, byte[])} to execute the request. * * @return The result of calling {@link AsyncClientHttpRequestExecution#executeAsync(HttpRequest, byte[])}. */ protected ListenableFuture<ClientHttpResponse> propagateTracingHeadersAndExecute( HttpRequestWrapperWithModifiableHeaders wrapperRequest, byte[] body, AsyncClientHttpRequestExecution execution ) throws IOException { propagateTracingHeaders(wrapperRequest, Tracer.getInstance().getCurrentSpan()); // Execute the request/interceptor chain. return execution.executeAsync(wrapperRequest, body); }
/** * Returns the name that should be used for the subspan surrounding the call. Defaults to whatever {@link * HttpTagAndSpanNamingStrategy#getInitialSpanName(Object, HttpTagAndSpanNamingAdapter)} returns, with a fallback * of {@link HttpRequestTracingUtils#getFallbackSpanNameForHttpRequest(String, String)} if the naming strategy * returned null or blank string. You can override this method to return something else if you want different * behavior and you don't want to adjust the naming strategy or adapter. * * @param request The request that is about to be executed. * @param namingStrategy The {@link HttpTagAndSpanNamingStrategy} being used. * @param adapter The {@link HttpTagAndSpanNamingAdapter} being used. * @return The name that should be used for the subspan surrounding the call. */ protected @NotNull String getSubspanSpanName( @NotNull HttpRequest request, @NotNull HttpTagAndSpanNamingStrategy<HttpRequest, ?> namingStrategy, @NotNull HttpTagAndSpanNamingAdapter<HttpRequest, ?> adapter ) { // Try the naming strategy first. String subspanNameFromStrategy = namingStrategy.getInitialSpanName(request, adapter); if (StringUtils.isNotBlank(subspanNameFromStrategy)) { return subspanNameFromStrategy; } // The naming strategy didn't have anything for us. Fall back to something reasonable. return HttpRequestTracingUtils.getFallbackSpanNameForHttpRequest( "asyncresttemplate_downstream_call", getRequestMethodAsString(request.getMethod()) ); }
@Test public void successCallbackWithTracing_using_current_thread_info_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = setupCurrentThreadWithTracingInfo(); // when SuccessCallback result = successCallbackWithTracing(successCallbackMock); // then verifySuccessCallbackWithTracing(result, successCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }
@Test public void failureCallbackWithTracing_pair_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = generateTracingInfo(); // when FailureCallback result = failureCallbackWithTracing(failureCallbackMock, setupInfo); // then verifyFailureCallbackWithTracing(result, failureCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }
@Test public void listenableFutureCallbackWithTracing_using_current_thread_info_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = setupCurrentThreadWithTracingInfo(); // when ListenableFutureCallback result = listenableFutureCallbackWithTracing(listenableFutureCallbackMock); // then verifyListenableFutureCallbackWithTracing(result, listenableFutureCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }
/** * Calls {@link WingtipsSpringUtil#propagateTracingHeaders(HttpMessage, Span)} to propagate the current span's * tracing state on the given request's headers, then returns * {@link ClientHttpRequestExecution#execute(HttpRequest, byte[])} to execute the request. * * @return The result of calling {@link ClientHttpRequestExecution#execute(HttpRequest, byte[])}. */ protected ClientHttpResponse propagateTracingHeadersAndExecuteRequest( HttpRequestWrapperWithModifiableHeaders wrapperRequest, byte[] body, ClientHttpRequestExecution execution ) throws IOException { propagateTracingHeaders(wrapperRequest, Tracer.getInstance().getCurrentSpan()); // Execute the request/interceptor chain. return execution.execute(wrapperRequest, body); }
/** * Returns the name that should be used for the subspan surrounding the call. Defaults to whatever {@link * HttpTagAndSpanNamingStrategy#getInitialSpanName(Object, HttpTagAndSpanNamingAdapter)} returns, with a fallback * of {@link HttpRequestTracingUtils#getFallbackSpanNameForHttpRequest(String, String)} if the naming strategy * returned null or blank string. You can override this method to return something else if you want different * behavior and you don't want to adjust the naming strategy or adapter. * * @param request The request that is about to be executed. * @param namingStrategy The {@link HttpTagAndSpanNamingStrategy} being used. * @param adapter The {@link HttpTagAndSpanNamingAdapter} being used. * @return The name that should be used for the subspan surrounding the call. */ protected @NotNull String getSubspanSpanName( @NotNull HttpRequest request, @NotNull HttpTagAndSpanNamingStrategy<HttpRequest, ?> namingStrategy, @NotNull HttpTagAndSpanNamingAdapter<HttpRequest, ?> adapter ) { // Try the naming strategy first. String subspanNameFromStrategy = namingStrategy.getInitialSpanName(request, adapter); if (StringUtils.isNotBlank(subspanNameFromStrategy)) { return subspanNameFromStrategy; } // The naming strategy didn't have anything for us. Fall back to something reasonable. return HttpRequestTracingUtils.getFallbackSpanNameForHttpRequest( "asyncresttemplate_downstream_call", getRequestMethodAsString(request.getMethod()) ); }
@Test public void successCallbackWithTracing_pair_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = generateTracingInfo(); // when SuccessCallback result = successCallbackWithTracing(successCallbackMock, setupInfo); // then verifySuccessCallbackWithTracing(result, successCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }
@Test public void failureCallbackWithTracing_using_current_thread_info_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = setupCurrentThreadWithTracingInfo(); // when FailureCallback result = failureCallbackWithTracing(failureCallbackMock); // then verifyFailureCallbackWithTracing(result, failureCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }
@Test public void listenableFutureCallbackWithTracing_pair_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = generateTracingInfo(); // when ListenableFutureCallback result = listenableFutureCallbackWithTracing(listenableFutureCallbackMock, setupInfo); // then verifyListenableFutureCallbackWithTracing(result, listenableFutureCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }
@GetMapping(path = NESTED_ASYNC_CALL_PATH) @SuppressWarnings("unused") public DeferredResult<EndpointSpanInfoDto> getNestedAsyncCall() { DeferredResult<EndpointSpanInfoDto> asyncResponse = new DeferredResult<>(); executor.execute(runnableWithTracing(() -> { try { logger.info("Nested async call endpoint hit. Sleeping..."); sleepThread(SLEEP_TIME_MILLIS); URI nestedCallUri = URI.create( "http://localhost:" + serverPort + SPAN_INFO_CALL_PATH + "?someQuery=foobar" ); logger.info("...Calling: " + nestedCallUri.toString()); ListenableFuture<ResponseEntity<EndpointSpanInfoDto>> asyncRestTemplateResultFuture = wingtipsEnabledAsyncRestTemplate.exchange( nestedCallUri, HttpMethod.GET, getHttpEntityWithUserIdHeader(), EndpointSpanInfoDto.class ); asyncRestTemplateResultFuture.addCallback( successCallbackWithTracing(result -> { logger.info("AsyncRestTemplate call complete"); asyncResponse.setResult(result.getBody()); }), failureCallbackWithTracing(asyncResponse::setErrorResult) ); } catch(Throwable t) { asyncResponse.setErrorResult(t); } })); return asyncResponse; }
/** * @return A new {@link RestTemplate} instance with a {@link WingtipsClientHttpRequestInterceptor} already added * and configured to surround downstream calls with a subspan, and using the default * {@link HttpTagAndSpanNamingStrategy} and {@link HttpTagAndSpanNamingAdapter} ({@link ZipkinHttpTagStrategy} and * {@link SpringHttpClientTagAdapter}). */ public static RestTemplate createTracingEnabledRestTemplate() { return createTracingEnabledRestTemplate(true); }
/** * @return A new {@link AsyncRestTemplate} instance with a {@link WingtipsAsyncClientHttpRequestInterceptor} * already added and configured to surround downstream calls with a subspan, and using the default * {@link HttpTagAndSpanNamingStrategy} and {@link HttpTagAndSpanNamingAdapter} ({@link ZipkinHttpTagStrategy} and * {@link SpringHttpClientTagAdapter}). */ public static AsyncRestTemplate createTracingEnabledAsyncRestTemplate() { return createTracingEnabledAsyncRestTemplate(true); }
/** * Calls {@link WingtipsSpringUtil#propagateTracingHeaders(HttpMessage, Span)} to propagate the current span's * tracing state on the given request's headers, then returns * {@link ClientHttpRequestExecution#execute(HttpRequest, byte[])} to execute the request. * * @return The result of calling {@link ClientHttpRequestExecution#execute(HttpRequest, byte[])}. */ protected ClientHttpResponse propagateTracingHeadersAndExecuteRequest( HttpRequestWrapperWithModifiableHeaders wrapperRequest, byte[] body, ClientHttpRequestExecution execution ) throws IOException { propagateTracingHeaders(wrapperRequest, Tracer.getInstance().getCurrentSpan()); // Execute the request/interceptor chain. return execution.execute(wrapperRequest, body); }
/** * Returns the name that should be used for the subspan surrounding the call. Defaults to whatever {@link * HttpTagAndSpanNamingStrategy#getInitialSpanName(Object, HttpTagAndSpanNamingAdapter)} returns, with a fallback * of {@link HttpRequestTracingUtils#getFallbackSpanNameForHttpRequest(String, String)} if the naming strategy * returned null or blank string. You can override this method to return something else if you want different * behavior and you don't want to adjust the naming strategy or adapter. * * @param request The request that is about to be executed. * @param namingStrategy The {@link HttpTagAndSpanNamingStrategy} being used. * @param adapter The {@link HttpTagAndSpanNamingAdapter} being used. * @return The name that should be used for the subspan surrounding the call. */ protected @NotNull String getSubspanSpanName( @NotNull HttpRequest request, @NotNull HttpTagAndSpanNamingStrategy<HttpRequest, ?> namingStrategy, @NotNull HttpTagAndSpanNamingAdapter<HttpRequest, ?> adapter ) { // Try the naming strategy first. String subspanNameFromStrategy = namingStrategy.getInitialSpanName(request, adapter); if (StringUtils.isNotBlank(subspanNameFromStrategy)) { return subspanNameFromStrategy; } // The naming strategy didn't have anything for us. Fall back to something reasonable. return HttpRequestTracingUtils.getFallbackSpanNameForHttpRequest( "resttemplate_downstream_call", getRequestMethodAsString(request.getMethod()) ); } }
@Test public void successCallbackWithTracing_separate_args_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = generateTracingInfo(); // when SuccessCallback result = successCallbackWithTracing( successCallbackMock, setupInfo.getLeft(), setupInfo.getRight() ); // then verifySuccessCallbackWithTracing(result, successCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }
@Test public void failureCallbackWithTracing_separate_args_works_as_expected() { // given Pair<Deque<Span>, Map<String, String>> setupInfo = generateTracingInfo(); // when FailureCallback result = failureCallbackWithTracing( failureCallbackMock, setupInfo.getLeft(), setupInfo.getRight() ); // then verifyFailureCallbackWithTracing(result, failureCallbackMock, setupInfo.getLeft(), setupInfo.getRight()); }