@Override protected void doHandleResponseAndErrorTagging( @NotNull Span span, @Nullable REQ request, @Nullable RES response, @Nullable Throwable error, @NotNull HttpTagAndSpanNamingAdapter<REQ, RES> adapter ) { // Now that we have both request and response, we'll re-try to get the route. putTagIfValueIsNotBlank(span, KnownZipkinTags.HTTP_ROUTE, adapter.getRequestUriPathTemplate(request, response)); putTagIfValueIsNotBlank(span, KnownZipkinTags.HTTP_STATUS_CODE, adapter.getResponseHttpStatus(response)); // For error tagging, we'll defer to the error Throwable if it's not null. if (error != null) { String message = error.getMessage(); if (message == null) { message = error.getClass().getSimpleName(); } addErrorTagToSpan(span, message); } else { // The error Throwable was null, so we'll see if the adapter thinks this is an error response. String errorTagValue = adapter.getErrorResponseTagValue(response); if (StringUtils.isNotBlank(errorTagValue)) { addErrorTagToSpan(span, errorTagValue); } } }
@NotNull HttpTagAndSpanNamingAdapter<REQ, RES> adapter ) { String finalSpanName = adapter.getFinalSpanName(request, response);
/** * Returns the span name that should be used for the given request according to this strategy and/or the given * adapter. By default this will delegate to the adapter's {@link * HttpTagAndSpanNamingAdapter#getInitialSpanName(Object)} unless you override this method. * * <p><b>NOTE: This method may return null if the strategy and adapter have no opinion, or if something goes wrong. * Callers must always check for a null return value, and generate a backup span name in those cases.</b> * * <p>This method does the actual work for the public-facing {@link * #getInitialSpanName(Object, HttpTagAndSpanNamingAdapter)}. * * @param request The incoming request - will never be null. * @param adapter The adapter to handle the incoming request - will never be null. * * @return The span name that should be used for the given request according to this strategy and/or the given * adapter, or null if the span name should be deferred to the caller. */ protected @Nullable String doGetInitialSpanName( @NotNull REQ request, @NotNull HttpTagAndSpanNamingAdapter<REQ, ?> adapter ) { return adapter.getInitialSpanName(request); }
@Override protected void doHandleRequestTagging( @NotNull Span span, @NotNull REQ request, @NotNull HttpTagAndSpanNamingAdapter<REQ, ?> adapter ) { putTagIfValueIsNotBlank(span, KnownOpenTracingTags.HTTP_METHOD, adapter.getRequestHttpMethod(request)); putTagIfValueIsNotBlank(span, KnownOpenTracingTags.HTTP_URL, adapter.getRequestUrl(request)); }
@Override protected void doHandleRequestTagging( @NotNull Span span, @NotNull REQ request, @NotNull HttpTagAndSpanNamingAdapter<REQ, ?> adapter ) { putTagIfValueIsNotBlank(span, KnownZipkinTags.HTTP_METHOD, adapter.getRequestHttpMethod(request)); putTagIfValueIsNotBlank(span, KnownZipkinTags.HTTP_PATH, adapter.getRequestPath(request)); putTagIfValueIsNotBlank(span, KnownZipkinTags.HTTP_URL, adapter.getRequestUrl(request)); putTagIfValueIsNotBlank(span, KnownZipkinTags.HTTP_ROUTE, adapter.getRequestUriPathTemplate(request, null)); }
@Override protected void doHandleResponseAndErrorTagging( @NotNull Span span, @Nullable REQ request, @Nullable RES response, @Nullable Throwable error, @NotNull HttpTagAndSpanNamingAdapter<REQ, RES> adapter ) { putTagIfValueIsNotBlank(span, KnownOpenTracingTags.HTTP_STATUS, adapter.getResponseHttpStatus(response)); if (error != null || StringUtils.isNotBlank(adapter.getErrorResponseTagValue(response))) { // OpenTracing doesn't expect you to pass messages with the error tag, just error=true. // So we don't need to do anything with the given error Throwable or returned // getErrorResponseTagValue(), other than have them trigger adding the error=true tag. span.putTag(KnownOpenTracingTags.ERROR, "true"); } } }
) { doReturn(prefix).when(implSpy).getSpanNamePrefix(anyObject()); doReturn(httpMethod).when(implSpy).getRequestHttpMethod(anyObject()); doReturn(pathTemplate).when(implSpy).getRequestUriPathTemplate(anyObject(), anyObject()); doReturn(responseStatusCode).when(implSpy).getResponseHttpStatus(anyObject()); String initialSpanNameResult = implSpy.getInitialSpanName(new Object()); String finalSpanNameResult = implSpy.getFinalSpanName(new Object(), new Object());
@NotNull HttpTagAndSpanNamingAdapter<REQ, RES> adapter ) { String httpMethod = (adapter == null) ? null : adapter.getRequestHttpMethod(request); String pathTemplate = (adapter == null) ? null : adapter.getRequestUriPathTemplate(request, response); Integer responseStatusCode = (adapter == null) ? null : adapter.getResponseHttpStatus(response);
/** * By default this method uses a combination of {@link #getSpanNamePrefix(Object)} and {@link * HttpRequestTracingUtils#generateSafeSpanName(Object, Object, HttpTagAndSpanNamingAdapter)} to generate a name. * You can override this method if you need different behavior. * * @param request The request object to be inspected - <b>this may be null!</b> * @param response The response object to be inspected - <b>this may be null!</b> * * @return The final span name that this adapter wants to use for a span around the given request, or null * if this adapter can't (or doesn't want to) come up with a name. Callers should always check for a null return * value, and should not change the preexisting initial span name if this returns null. */ public @Nullable String getFinalSpanName(@Nullable REQ request, @Nullable RES response) { String prefix = getSpanNamePrefix(request); String defaultSpanName = HttpRequestTracingUtils.generateSafeSpanName(request, response, this); return (StringUtils.isBlank(prefix)) ? defaultSpanName : prefix + "-" + defaultSpanName; }
/** * Adds any extra tags that Wingtips wants to add that aren't normally a part of specific tag strategies like * Zipkin or OpenTracing. * * <p>By default this will add the {@link WingtipsTags#SPAN_HANDLER} tag with the value that comes from * {@link HttpTagAndSpanNamingAdapter#getSpanHandlerTagValue(Object, Object)}. * * <p>This method does the actual extra-wingtips-tagging work for the public-facing {@link * #handleResponseTaggingAndFinalSpanName(Span, Object, Object, Throwable, HttpTagAndSpanNamingAdapter)}. * * @param span The span to tag - will never be null. * @param request The request object - this may be null. * @param response The response object - this may be null. * @param error The error that prevented the request/response from completing normally, or null if no such error * occurred. * @param adapter The adapter to handle the request and response - will never be null. */ @SuppressWarnings("unused") protected void doExtraWingtipsTagging( @NotNull Span span, @Nullable REQ request, @Nullable RES response, @Nullable Throwable error, @NotNull HttpTagAndSpanNamingAdapter<REQ, RES> adapter ) { putTagIfValueIsNotBlank(span, WingtipsTags.SPAN_HANDLER, adapter.getSpanHandlerTagValue(request, response)); }
/** * Returns the value that should be used for the "error" {@link com.nike.wingtips.Span#putTag(String, String)} * associated with the given response, or null if this response does not indicate an error. The criteria for * determining an error can change depending on whether it's a client span or server span, or there may even be * project-specific logic. * * <p>By default, this method considers a response to indicate an error if the {@link * #getResponseHttpStatus(Object)} is greater than or equal to 400, and will return that status code as a string. * This is a client-span-centric view - implementations of this class that represent server spans may want to * override this to have it only consider status codes greater than or equal to 500 to be errors. * * <p>NOTE: It's important that you return something non-empty and non-blank if want the span to be considered an * error. In particular, empty strings or strings that consist of only whitespace may be treated by callers * of this method the same as {@code null}. * * @param response The response object to be inspected - <b>this may be null!</b> * * @return The value that should be used for the "error" {@link com.nike.wingtips.Span#putTag(String, String)} * associated with the given response, or null if this response does not indicate an error. */ public @Nullable String getErrorResponseTagValue(@Nullable RES response) { Integer statusCode = getResponseHttpStatus(response); if (statusCode != null && statusCode >= 400) { return statusCode.toString(); } // Status code does not indicate an error, so return null. return null; }
@Test public void doHandleRequestTagging_puts_expected_tags_based_on_adapter_results() { // given String adapterHttpMethod = "httpmethod-" + UUID.randomUUID().toString(); String adapterPath = "path-" + UUID.randomUUID().toString(); String adapterHttpUrl = "url-" + UUID.randomUUID().toString(); String adapterRoute = "route-" + UUID.randomUUID().toString(); doReturn(adapterHttpMethod).when(adapterMock).getRequestHttpMethod(anyObject()); doReturn(adapterPath).when(adapterMock).getRequestPath(anyObject()); doReturn(adapterHttpUrl).when(adapterMock).getRequestUrl(anyObject()); doReturn(adapterRoute).when(adapterMock).getRequestUriPathTemplate(anyObject(), anyObject()); // when implSpy.doHandleRequestTagging(spanMock, requestMock, adapterMock); // then verify(adapterMock).getRequestHttpMethod(requestMock); verify(adapterMock).getRequestPath(requestMock); verify(adapterMock).getRequestUrl(requestMock); verify(adapterMock).getRequestUriPathTemplate(requestMock, null); verify(implSpy).putTagIfValueIsNotBlank(spanMock, KnownZipkinTags.HTTP_METHOD, adapterHttpMethod); verify(implSpy).putTagIfValueIsNotBlank(spanMock, KnownZipkinTags.HTTP_PATH, adapterPath); verify(implSpy).putTagIfValueIsNotBlank(spanMock, KnownZipkinTags.HTTP_URL, adapterHttpUrl); verify(implSpy).putTagIfValueIsNotBlank(spanMock, KnownZipkinTags.HTTP_ROUTE, adapterRoute); }
HttpTagAndSpanNamingAdapter<Object, Object> adapterMock = mock(HttpTagAndSpanNamingAdapter.class); doReturn(httpMethod).when(adapterMock).getRequestHttpMethod(anyObject()); doReturn(pathTemplate).when(adapterMock).getRequestUriPathTemplate(anyObject(), anyObject()); doReturn(statusCode).when(adapterMock).getResponseHttpStatus(anyObject()); assertThat(directArgsResult).isEqualTo(expectedResult); verify(adapterMock).getRequestHttpMethod(requestMock); verify(adapterMock).getRequestUriPathTemplate(requestMock, responseMock); verify(adapterMock).getResponseHttpStatus(responseMock); verifyNoMoreInteractions(adapterMock); verifyZeroInteractions(requestMock, responseMock);
@DataProvider(value = { "null | null", "0 | null", "100 | null", "200 | null", "300 | null", "399 | null", "400 | 400", "499 | 499", "500 | 500", "599 | 599", "999 | 999", }, splitBy = "\\|") @Test public void getErrorResponseTagValue_works_as_expected(Integer responseStatusCode, String expectedReturnVal) { // given doReturn(responseStatusCode).when(implSpy).getResponseHttpStatus(anyObject()); // when // Null response object makes no difference - it's entirely dependent on what getResponseHttpStatus() returns. String resultForNonNullResponseObj = implSpy.getErrorResponseTagValue(new Object()); String resultForNullResponseObj = implSpy.getErrorResponseTagValue(null); // then assertThat(resultForNonNullResponseObj).isEqualTo(expectedReturnVal); assertThat(resultForNullResponseObj).isEqualTo(expectedReturnVal); }
@Test public void doHandleRequestTagging_puts_expected_tags_based_on_adapter_results() { // given String adapterHttpMethod = "httpmethod-" + UUID.randomUUID().toString(); String adapterHttpUrl = "url-" + UUID.randomUUID().toString(); doReturn(adapterHttpMethod).when(adapterMock).getRequestHttpMethod(anyObject()); doReturn(adapterHttpUrl).when(adapterMock).getRequestUrl(anyObject()); // when implSpy.doHandleRequestTagging(spanMock, requestMock, adapterMock); // then verify(adapterMock).getRequestHttpMethod(requestMock); verify(adapterMock).getRequestUrl(requestMock); verify(implSpy).putTagIfValueIsNotBlank(spanMock, KnownOpenTracingTags.HTTP_METHOD, adapterHttpMethod); verify(implSpy).putTagIfValueIsNotBlank(spanMock, KnownOpenTracingTags.HTTP_URL, adapterHttpUrl); }
/** * By default this method uses a combination of {@link #getSpanNamePrefix(Object)} and {@link * HttpRequestTracingUtils#generateSafeSpanName(Object, Object, HttpTagAndSpanNamingAdapter)} to generate a name. * You can override this method if you need different behavior. * * @param request The request object to be inspected - <b>this may be null!</b> * * @return The initial span name that this adapter wants to use for a span around the given request, or null * if this adapter can't (or doesn't want to) come up with a name. Callers should always check for a null return * value, and come up with a reasonable fallback name in that case. */ public @Nullable String getInitialSpanName(@Nullable REQ request) { String prefix = getSpanNamePrefix(request); String defaultSpanName = HttpRequestTracingUtils.generateSafeSpanName(request, null, this); return (StringUtils.isBlank(prefix)) ? defaultSpanName : prefix + "-" + defaultSpanName; }
@Test public void doExtraWingtipsTagging_adds_SPAN_HANDLER_tag_if_adapter_getSpanHandlerTagValue_is_not_blank() { // given String adapterSpanHandlerTagValue = UUID.randomUUID().toString(); doReturn(adapterSpanHandlerTagValue).when(adapterMock).getSpanHandlerTagValue(anyObject(), anyObject()); // when implSpy.doExtraWingtipsTagging(spanMock, requestObjectMock, responseObjectMock, errorMock, adapterMock); // then verify(implSpy).putTagIfValueIsNotBlank(spanMock, WingtipsTags.SPAN_HANDLER, adapterSpanHandlerTagValue); verify(spanMock).putTag(WingtipsTags.SPAN_HANDLER, adapterSpanHandlerTagValue); verifyNoMoreInteractions(spanMock); }
String adapterRoute = "route-" + UUID.randomUUID().toString(); doReturn(adapterHttpStatus).when(adapterMock).getResponseHttpStatus(anyObject()); doReturn(adapterRoute).when(adapterMock).getRequestUriPathTemplate(anyObject(), anyObject()); doReturn(scenario.adapterErrorTagValue).when(adapterMock).getErrorResponseTagValue(anyObject()); verify(adapterMock).getRequestUriPathTemplate(requestMock, responseMock); verify(adapterMock).getResponseHttpStatus(responseMock); verify(adapterMock).getErrorResponseTagValue(responseMock); verify(adapterMock, never()).getErrorResponseTagValue(anyObject());
doReturn(adapterHttpStatus).when(adapterMock).getResponseHttpStatus(anyObject()); doReturn(scenario.adapterErrorTagValue).when(adapterMock).getErrorResponseTagValue(anyObject()); verify(adapterMock).getResponseHttpStatus(responseMock); verify(adapterMock).getErrorResponseTagValue(responseMock); verify(adapterMock, never()).getErrorResponseTagValue(anyObject());
@Test public void doDetermineAndSetFinalSpanName_delegates_to_adapter_getFinalSpanName_and_changes_span_name_if_result_is_not_blank() { // given String adapterSpanNameResult = UUID.randomUUID().toString(); doReturn(adapterSpanNameResult).when(adapterMock).getFinalSpanName(anyObject(), anyObject()); Span span = Span.newBuilder("originalSpanName", SpanPurpose.SERVER).build(); assertThat(span.getSpanName()).isNotEqualTo(adapterSpanNameResult); // when implSpy.doDetermineAndSetFinalSpanName(span, requestObjectMock, responseObjectMock, errorMock, adapterMock); // then assertThat(span.getSpanName()).isEqualTo(adapterSpanNameResult); verify(adapterMock).getFinalSpanName(requestObjectMock, responseObjectMock); }