RequestTracingFilter implements Filter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { TracingState originalThreadInfo = TracingState.getCurrentThreadTracingState(); try { Span overallRequestSpan = createNewSpanForRequest(request); addTracingInfoToRequestAttributes(overallRequestSpan, request); response.setHeader(TraceHeaders.TRACE_ID, overallRequestSpan.getTraceId()); TracingState originalRequestTracingState = TracingState.getCurrentThreadTracingState(); Throwable errorForTagging = null; try { tagAndNamingStrategy.handleRequestTagging(overallRequestSpan, request, tagAndNamingAdapter); filterChain.doFilter(request, response); } catch(Throwable t) { errorForTagging = t; throw t; } finally { if (isAsyncRequest(request)) { setupTracingCompletionWhenAsyncRequestCompletes( request, response, originalRequestTracingState, tagAndNamingStrategy, tagAndNamingAdapter ); } else { try { tagAndNamingStrategy.handleResponseTaggingAndFinalSpanName( overallRequestSpan, request, response, errorForTagging, tagAndNamingAdapter ); } finally { Tracer.getInstance().completeRequestSpan(); } } } } finally { unlinkTracingFromCurrentThread(originalThreadInfo); } }  @Override @SuppressWarnings("RedundantThrows") void init(FilterConfig filterConfig); @Override void destroy(); @Override void doFilter(ServletRequest request,
                         ServletResponse response,
                         FilterChain filterChain); static final String FILTER_HAS_ALREADY_EXECUTED_ATTRIBUTE; @Deprecated
    static final String ERROR_REQUEST_URI_ATTRIBUTE; static final String USER_ID_HEADER_KEYS_LIST_INIT_PARAM_NAME; static final String TAG_AND_SPAN_NAMING_STRATEGY_INIT_PARAM_NAME; static final String TAG_AND_SPAN_NAMING_ADAPTER_INIT_PARAM_NAME; }
@Test public void doFilterInternal_should_create_new_sampleable_span_if_no_parent_in_request_and_it_should_be_completed_and_tags_should_be_handled( ) throws ServletException, IOException { RequestTracingFilter filter = getBasicFilter(); filter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain); Span span = spanCapturingFilterChain.capturedSpan; assertThat(span).isNotNull(); assertThat(span.getTraceId()).isNotNull(); assertThat(span.getSpanId()).isNotNull(); assertThat(span.getSpanName()).isNotNull(); assertThat(span.getParentSpanId()).isNull(); assertThat(span.isSampleable()).isTrue(); assertThat(span.isCompleted()).isTrue(); assertThat(strategyRequestTaggingMethodCalled.get()).isTrue(); strategyRequestTaggingArgs.get().verifyArgs(span, requestMock, filter.tagAndNamingAdapter); assertThat(strategyResponseTaggingAndFinalSpanNameMethodCalled.get()).isTrue(); strategyResponseTaggingArgs.get().verifyArgs( span, requestMock, responseMock, null, filter.tagAndNamingAdapter ); }
@Test public void doFilterInternal_should_not_complete_span_or_response_tags_until_after_filter_chain_runs( ) throws ServletException, IOException { RequestTracingFilter filter = getBasicFilter(); AtomicBoolean spanCompletedHolder = new AtomicBoolean(false); AtomicReference<Span> spanHolder = new AtomicReference<>(); AtomicReference<Boolean> requestTagsExecutedAtTimeOfFilterChain = new AtomicReference<>(); AtomicReference<Boolean> responseTagsExecutedAtTimeOfFilterChain = new AtomicReference<>(); FilterChain smartFilterChain = (request, response) -> { Span span = Tracer.getInstance().getCurrentSpan(); spanHolder.set(span); if (span != null) { spanCompletedHolder.set(span.isCompleted()); } requestTagsExecutedAtTimeOfFilterChain.set(strategyRequestTaggingMethodCalled.get()); responseTagsExecutedAtTimeOfFilterChain.set(strategyResponseTaggingAndFinalSpanNameMethodCalled.get()); }; filter.doFilterInternal(requestMock, responseMock, smartFilterChain); assertThat(spanHolder.get()).isNotNull(); assertThat(spanCompletedHolder.get()).isFalse(); assertThat(spanHolder.get().isCompleted()).isTrue(); assertThat(requestTagsExecutedAtTimeOfFilterChain.get()).isTrue(); assertThat(responseTagsExecutedAtTimeOfFilterChain.get()).isFalse(); }
@Test public void doFilterInternal_should_set_request_attributes_to_new_span_info_with_user_id( ) throws ServletException, IOException { RequestTracingFilter spyFilter = spy(getBasicFilter()); given(requestMock.getHeader(USER_ID_HEADER_KEY)).willReturn("testUserId"); spyFilter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain); assertThat(spanCapturingFilterChain.capturedSpan).isNotNull(); Span newSpan = spanCapturingFilterChain.capturedSpan; assertThat(newSpan.getUserId()).isEqualTo("testUserId"); }
@Test public void doFilterInternal_should_set_request_attributes_to_new_span_info_with_alt_user_id( ) throws ServletException, IOException { RequestTracingFilter spyFilter = spy(getBasicFilter()); given(requestMock.getHeader(ALT_USER_ID_HEADER_KEY)).willReturn("testUserId"); spyFilter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain); assertThat(spanCapturingFilterChain.capturedSpan).isNotNull(); Span newSpan = spanCapturingFilterChain.capturedSpan; assertThat(newSpan.getUserId()).isEqualTo("testUserId"); }
@Test public void doFilterInternal_should_set_request_attributes_to_new_span_info() throws ServletException, IOException { RequestTracingFilter filter = getBasicFilter(); filter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain); assertThat(spanCapturingFilterChain.capturedSpan).isNotNull(); Span newSpan = spanCapturingFilterChain.capturedSpan; verify(requestMock).setAttribute(TraceHeaders.TRACE_SAMPLED, newSpan.isSampleable()); verify(requestMock).setAttribute(TraceHeaders.TRACE_ID, newSpan.getTraceId()); verify(requestMock).setAttribute(TraceHeaders.SPAN_ID, newSpan.getSpanId()); verify(requestMock).setAttribute(TraceHeaders.PARENT_SPAN_ID, newSpan.getParentSpanId()); verify(requestMock).setAttribute(TraceHeaders.SPAN_NAME, newSpan.getSpanName()); verify(requestMock).setAttribute(Span.class.getName(), newSpan); }
@Test public void doFilterInternal_should_set_trace_id_in_response_header() throws ServletException, IOException { RequestTracingFilter filter = getBasicFilter(); filter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain); assertThat(spanCapturingFilterChain.capturedSpan).isNotNull(); verify(responseMock).setHeader(TraceHeaders.TRACE_ID, spanCapturingFilterChain.capturedSpan.getTraceId()); }
@Test public void doFilterInternal_should_use_user_id_from_parent_span_info_if_present_in_request_headers( ) throws ServletException, IOException { RequestTracingFilter spyFilter = spy(getBasicFilter()); given(requestMock.getHeader(ALT_USER_ID_HEADER_KEY)).willReturn("testUserId"); Span parentSpan = Span.newBuilder("someParentSpan", null) .withParentSpanId(TraceAndSpanIdGenerator.generateId()) .withSampleable(false) .withUserId("someUser") .build(); given(requestMock.getHeader(TraceHeaders.TRACE_ID)).willReturn(parentSpan.getTraceId()); given(requestMock.getHeader(TraceHeaders.SPAN_ID)).willReturn(parentSpan.getSpanId()); given(requestMock.getHeader(TraceHeaders.PARENT_SPAN_ID)).willReturn(parentSpan.getParentSpanId()); given(requestMock.getHeader(TraceHeaders.SPAN_NAME)).willReturn(parentSpan.getSpanName()); given(requestMock.getHeader(TraceHeaders.TRACE_SAMPLED)).willReturn(String.valueOf(parentSpan.isSampleable())); given(requestMock.getServletPath()).willReturn("/some/path"); given(requestMock.getMethod()).willReturn("GET"); spyFilter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain); assertThat(spanCapturingFilterChain.capturedSpan).isNotNull(); Span newSpan = spanCapturingFilterChain.capturedSpan; assertThat(newSpan.getUserId()).isEqualTo("testUserId"); }
@DataProvider(value = { "true | true", "true | false", "false | true", "false | false", }, splitBy = "\\|") @Test public void doFilterInternal_should_reset_tracing_info_to_whatever_was_on_the_thread_originally( boolean isAsync, boolean throwExceptionInInnerFinallyBlock ) { final RequestTracingFilter filter = getBasicFilter(); if (isAsync) { setupAsyncContextWorkflow(); } RuntimeException exToThrowInInnerFinallyBlock = null; if (throwExceptionInInnerFinallyBlock) { exToThrowInInnerFinallyBlock = new RuntimeException("kaboom"); doThrow(exToThrowInInnerFinallyBlock).when(requestMock).isAsyncStarted(); } Tracer.getInstance().startRequestWithRootSpan("someOutsideSpan"); TracingState originalTracingState = TracingState.getCurrentThreadTracingState(); Throwable ex = catchThrowable( () -> filter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain) ); if (throwExceptionInInnerFinallyBlock) { assertThat(ex).isSameAs(exToThrowInInnerFinallyBlock); } assertThat(TracingState.getCurrentThreadTracingState()).isEqualTo(originalTracingState); assertThat(spanCapturingFilterChain.capturedSpan).isNotNull(); assertThat(spanCapturingFilterChain.capturedSpan.getTraceId()) .isNotEqualTo(originalTracingState.spanStack.peek().getTraceId()); }