@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); }
@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); }
.withUserId("testUserId") .build(); assertThat(Tracer.getInstance().getCurrentSpan()).isNull();
.withSpanName(spanName) .withSampleable(sampleableForFullyCompleteSpan) .withUserId(userId) .withSpanPurpose(spanPurpose) .withSpanStartTimeEpochMicros(startTimeEpochMicrosForFullyCompleteSpan)
.withParentSpanId(parentSpanId) .withSampleable(sampleable) .withUserId(userId) .build();
.withSpanId(getSpanIdFromRequest(request, TraceHeaders.SPAN_ID, true)) .withSampleable(getSpanSampleableFlag(request)) .withUserId(getUserIdFromRequestWithHeaders(request, userIdHeaderKeys)) .build();
.withParentSpanId(TraceAndSpanIdGenerator.generateId()) .withSampleable(false) .withUserId("someUser") .build(); given(requestMock.getHeader(TraceHeaders.TRACE_ID)).willReturn(parentSpan.getTraceId());
@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"); }
/** * @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(); }
/** * @return A {@link Span} object created from the headers if they exist (see {@link #fromHttpServletRequest(HttpServletRequest, List)} for details), or if the headers don't * have enough information then this will return a new root span with a span name based on the results of {@link #getSpanName(HttpServletRequest)} and user ID based * on the result of {@link #getUserIdFromHttpServletRequest(HttpServletRequest, List)}. Since this method is for a server receiving a request * the returned span's {@link Span#getSpanPurpose()} will be {@link SpanPurpose#SERVER}. */ public static Span fromHttpServletRequestOrCreateRootSpan(HttpServletRequest servletRequest, List<String> userIdHeaderKeys) { Span span = fromHttpServletRequest(servletRequest, userIdHeaderKeys); if (span == null) { span = Span .generateRootSpanForNewTrace(getSpanName(servletRequest), SpanPurpose.SERVER) .withUserId(getUserIdFromHttpServletRequest(servletRequest, userIdHeaderKeys)) .build(); } return span; }
private Pair<Span, Map<String, String>> generateUpstreamSpanHeaders(boolean includeUserId) { Span.Builder spanBuilder = Span.newBuilder("upstreamSpan", Span.SpanPurpose.CLIENT); if (includeUserId) { spanBuilder.withUserId("user-" + UUID.randomUUID().toString()); } Span span = spanBuilder.build(); MapBuilder<String, String> headersBuilder = MapBuilder .builder(TraceHeaders.TRACE_ID, span.getTraceId()) .put(TraceHeaders.SPAN_ID, span.getSpanId()) .put(TraceHeaders.SPAN_NAME, span.getSpanName()) .put(TraceHeaders.TRACE_SAMPLED, String.valueOf(span.isSampleable())); if (span.getUserId() != null) { headersBuilder.put(USER_ID_HEADER_KEY, span.getUserId()); } return Pair.of(span, headersBuilder.build()); }
private Pair<Span, Map<String, String>> generateUpstreamSpanHeaders(boolean includeUserId) { Span.Builder spanBuilder = Span.newBuilder("upstreamSpan", Span.SpanPurpose.CLIENT); if (includeUserId) { spanBuilder.withUserId("user-" + UUID.randomUUID().toString()); } Span span = spanBuilder.build(); MapBuilder<String, String> headersBuilder = MapBuilder .builder(TraceHeaders.TRACE_ID, span.getTraceId()) .put(TraceHeaders.SPAN_ID, span.getSpanId()) .put(TraceHeaders.SPAN_NAME, span.getSpanName()) .put(TraceHeaders.TRACE_SAMPLED, String.valueOf(span.isSampleable())); if (span.getUserId() != null) { headersBuilder.put(USER_ID_HEADER_KEY, span.getUserId()); } return Pair.of(span, headersBuilder.build()); }