@DataProvider(value = { "true", "false" }) @Test public void propagateTracingHeaders_only_sends_parent_span_id_header_if_parent_span_id_exists( boolean parentSpanIdExists ) { // given String parentSpanId = (parentSpanIdExists) ? UUID.randomUUID().toString() : null; Span span = Span.newBuilder("foo", SpanPurpose.CLIENT) .withParentSpanId(parentSpanId) .build(); // when WingtipsSpringUtil.propagateTracingHeaders(httpMessageMock, span); // then if (parentSpanIdExists) { verify(headersMock).set(PARENT_SPAN_ID, parentSpanId); } else { verify(headersMock, never()).set(eq(PARENT_SPAN_ID), anyString()); } }
.withTraceId(badTraceId) .withSpanId(badSpanId) .withParentSpanId(badParentSpanId) .withSpanStartTimeEpochMicros(Math.abs(random.nextLong())) .withDurationNanos(Math.abs(random.nextLong()))
? null : Span.newBuilder(UUID.randomUUID().toString(), SpanPurpose.CLIENT) .withParentSpanId(UUID.randomUUID().toString()) .build();
.withTraceId(traceId) .withSpanId(spanId) .withParentSpanId(parentSpanId) .withSpanName(spanName) .withSampleable(sampleableForFullyCompleteSpan)
.newBuilder(newSpanName, spanPurpose) .withTraceId(traceId) .withParentSpanId(parentSpanId) .withSampleable(sampleable) .withUserId(userId)
? null : spy(Span.newBuilder(UUID.randomUUID().toString(), Span.SpanPurpose.CLIENT) .withParentSpanId(UUID.randomUUID().toString()) .build());
.withParentSpanId(getSpanIdFromRequest(request, TraceHeaders.PARENT_SPAN_ID, false)) .withSpanId(getSpanIdFromRequest(request, TraceHeaders.SPAN_ID, true)) .withSampleable(getSpanSampleableFlag(request))
@Test public void convertSpanToKeyValueFormat_and_fromKeyValueString_should_escape_and_unescape_expected_non_tag_or_annotation_values() { // The TAGS_AND_ANNOTATIONS_WITH_SPECIAL_CHARS case already verified tags and annotations. Now we need to // verify that non-tag values are escaped. Also note that other tests have verified that escapeJson() // and unescapeJson() work properly. // given String complexSpanName = "span-name-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexTraceId = "trace-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexParentId = "parent-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexSpanId = "span-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexUserId = "user-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; Span span = Span.newBuilder(complexSpanName, SpanPurpose.CLIENT) .withTraceId(complexTraceId) .withParentSpanId(complexParentId) .withSpanId(complexSpanId) .withUserId(complexUserId) .build(); // when String keyValueStr = SpanParser.convertSpanToKeyValueFormat(span); // then assertThat(keyValueStr).contains("traceId=\"trace-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(keyValueStr).contains("parentSpanId=\"parent-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(keyValueStr).contains("spanId=\"span-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(keyValueStr).contains("userId=\"user-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(keyValueStr).contains("spanName=\"span-name-" + ESCAPED_JSON_CHARS + "\""); // and when Span deserialized = SpanParser.fromKeyValueString(keyValueStr); // then verifySpanDeepEquals(deserialized, span, true); }
.withParentSpanId(TraceAndSpanIdGenerator.generateId()) .withSampleable(false) .withUserId("someUser")
@Test public void convertSpanToJSON_and_fromJSON_should_escape_and_unescape_expected_non_tag_or_annotation_values() { // The TAGS_AND_ANNOTATIONS_WITH_SPECIAL_CHARS case already verified tags and annotations. Now we need to // verify that non-tag values are escaped. Also note that other tests have verified that escapeJson() // and unescapeJson() work properly. // given String complexSpanName = "span-name-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexTraceId = "trace-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexParentId = "parent-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexSpanId = "span-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; String complexUserId = "user-id-" + ALL_JSON_CHARS_THAT_NEED_ESCAPING; Span span = Span.newBuilder(complexSpanName, SpanPurpose.CLIENT) .withTraceId(complexTraceId) .withParentSpanId(complexParentId) .withSpanId(complexSpanId) .withUserId(complexUserId) .build(); // when String json = SpanParser.convertSpanToJSON(span); // then assertThat(json).contains("\"traceId\":\"trace-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(json).contains("\"parentSpanId\":\"parent-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(json).contains("\"spanId\":\"span-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(json).contains("\"userId\":\"user-id-" + ESCAPED_JSON_CHARS + "\""); assertThat(json).contains("\"spanName\":\"span-name-" + ESCAPED_JSON_CHARS + "\""); // and when Span deserialized = SpanParser.fromJSON(json); // then verifySpanDeepEquals(deserialized, span, true); }
? null : spy(Span.newBuilder(UUID.randomUUID().toString(), SpanPurpose.CLIENT) .withParentSpanId(UUID.randomUUID().toString()) .build());
@Test public void doFilterInternal_should_use_user_id_from_parent_span_info_if_present_in_request_headers( ) throws ServletException, IOException { // given: filter and request that has parent span info 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"); // when: doFilterInternal is called spyFilter.doFilterInternal(requestMock, responseMock, spanCapturingFilterChain); // then: the span that is created should use the parent span info as its parent assertThat(spanCapturingFilterChain.capturedSpan).isNotNull(); Span newSpan = spanCapturingFilterChain.capturedSpan; assertThat(newSpan.getUserId()).isEqualTo("testUserId"); }
@DataProvider(value = { "true", "false" }) @Test public void propagateTracingHeaders_only_sends_parent_span_id_header_if_parent_span_id_exists( boolean parentSpanIdExists ) { // given String parentSpanId = (parentSpanIdExists) ? UUID.randomUUID().toString() : null; Span span = Span.newBuilder("foo", Span.SpanPurpose.CLIENT) .withParentSpanId(parentSpanId) .build(); // when WingtipsApacheHttpClientUtil.propagateTracingHeaders(requestMock, span); // then if (parentSpanIdExists) { verify(requestMock).setHeader(PARENT_SPAN_ID, parentSpanId); } else { verify(requestMock, never()).setHeader(eq(PARENT_SPAN_ID), anyString()); } }
@DataProvider(value = { "true", "false" }) @Test public void propagateTracingHeaders_only_sends_parent_span_id_header_if_parent_span_id_exists( boolean parentSpanIdExists ) { // given String parentSpanId = (parentSpanIdExists) ? UUID.randomUUID().toString() : null; Span span = Span.newBuilder("foo", SpanPurpose.CLIENT) .withParentSpanId(parentSpanId) .build(); // when HttpRequestTracingUtils.propagateTracingHeaders(httpObjectForPropagationMock, span); // then if (parentSpanIdExists) { verify(httpObjectForPropagationMock).setHeader(PARENT_SPAN_ID, parentSpanId); } else { verify(httpObjectForPropagationMock, never()).setHeader(eq(PARENT_SPAN_ID), anyString()); } }
@UseDataProvider("idSanitizationScenarios") @Test public void convertWingtipsSpanToZipkinSpan_sanitizes_parentSpanId_as_expected_when_sanitization_is_enabled( IdSanitizationScenario scenario ) { // given impl = new WingtipsToZipkinSpanConverterDefaultImpl(true); final Endpoint zipkinEndpoint = Endpoint.newBuilder().serviceName(UUID.randomUUID().toString()).build(); final Span wingtipsSpan = Span.newBuilder("foo", SpanPurpose.CLIENT) .withParentSpanId(scenario.originalId) .withSpanStartTimeEpochMicros(Math.abs(random.nextLong())) .withDurationNanos(Math.abs(random.nextLong())) .build(); // when zipkin2.Span zipkinSpan = impl.convertWingtipsSpanToZipkinSpan(wingtipsSpan, zipkinEndpoint); // then assertThat(zipkinSpan.parentId()).isEqualTo(scenario.expectedSanitizedResultForSpanIdOrParentSpanId); assertThat(zipkinSpan.tags().get("invalid.parent_id")).isEqualTo(scenario.originalId); }
/** * @param spanName The {@link Span#getSpanName()} to use for the new child sub-span. * @param spanPurpose The {@link SpanPurpose} for the new child span. See the javadocs for {@link SpanPurpose} for full details on what each enum option * means. If you pass in null for this then {@link SpanPurpose#UNKNOWN} will be used. * @return A new uncompleted span representing a child of this instance. The returned instance's {@link #getParentSpanId()} will be this instance's * {@link #getSpanId()}, its {@link #getSpanName()} will be the given value, its {@link #getSpanId()} will be randomly generated, and its * {@link #getSpanStartTimeEpochMicros()} and {@link #getSpanStartTimeNanos()} values will be set to the appropriate values based on when this * method is called. It will share this instance's {@link #getTraceId()}, {@link #isSampleable()}, and {@link #getUserId()} values. */ public Span generateChildSpan(String spanName, SpanPurpose spanPurpose) { return Span.newBuilder(spanName, spanPurpose) .withTraceId(this.getTraceId()) .withSampleable(this.isSampleable()) .withUserId(this.getUserId()) .withParentSpanId(this.getSpanId()) .withSpanId(TraceAndSpanIdGenerator.generateId()) .withSpanStartTimeEpochMicros(TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis())) .withSpanStartTimeNanos(System.nanoTime()) .withDurationNanos(null) .build(); }
@Test public void unconfigureMDC_should_unset_span_values_on_MDC() throws Exception { // given Span span = Span.newBuilder("test-span", SpanPurpose.LOCAL_ONLY).withParentSpanId("3").build(); Tracer.configureMDC(span); // when Tracer.unconfigureMDC(); // then assertThat(MDC.get(Tracer.SPAN_JSON_MDC_KEY)).isNull(); }
@Test public void configureMDC_should_set_span_values_on_MDC() throws Exception { // given Span span = Span.newBuilder("test-span", SpanPurpose.LOCAL_ONLY).withParentSpanId("3").build(); String expected = span.toJSON(); // when Tracer.configureMDC(span); // then assertThat(MDC.get(Tracer.SPAN_JSON_MDC_KEY)).isEqualTo(expected); }