/** * @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; }
@Override public @Nullable String getRequestUriPathTemplate( @Nullable HttpServletRequest request, @Nullable HttpServletResponse response ) { return HttpSpanFactory.determineUriPathTemplate(request); }
String path = getRequestAttributeAsString(request, KnownZipkinTags.HTTP_ROUTE); path = getRequestAttributeAsString(request, SPRING_BEST_MATCHING_PATTERN_REQUEST_ATTRIBUTE_KEY);
/** * @param request The incoming request. * @return A new {@link Span} for the overall request. This inspects the incoming request's headers to determine * if it should continue an existing trace with a child span, or whether a brand new trace needs to be started. * {@link #getInitialSpanName(HttpServletRequest, HttpTagAndSpanNamingStrategy, HttpTagAndSpanNamingAdapter)} * is used to generate the initial span name. */ protected Span createNewSpanForRequest(HttpServletRequest request) { // See if there's trace info in the incoming request's headers. If so it becomes the parent trace. Tracer tracer = Tracer.getInstance(); final Span parentSpan = HttpSpanFactory.fromHttpServletRequest(request, getUserIdHeaderKeys()); Span newSpan; if (parentSpan != null) { logger.debug("Found parent Span {}", parentSpan); newSpan = tracer.startRequestWithChildSpan( parentSpan, getInitialSpanName(request, tagAndNamingStrategy, tagAndNamingAdapter) ); } else { newSpan = tracer.startRequestWithRootSpan( getInitialSpanName(request, tagAndNamingStrategy, tagAndNamingAdapter), HttpSpanFactory.getUserIdFromHttpServletRequest(request, getUserIdHeaderKeys()) ); logger.debug("Parent span not found, starting a new span {}", newSpan); } return newSpan; }
public EndpointSpanInfoDto(HttpServletRequest request, Span endpoint_execution_span, List<String> userIdHeaderKeys) { this.parent_span_info = new SpanInfoDto( request.getHeader(TraceHeaders.TRACE_ID), request.getHeader(TraceHeaders.SPAN_ID), request.getHeader(TraceHeaders.PARENT_SPAN_ID), request.getHeader(TraceHeaders.TRACE_SAMPLED), HttpSpanFactory.getUserIdFromHttpServletRequest(request, userIdHeaderKeys) ); this.endpoint_execution_span_info = new SpanInfoDto( endpoint_execution_span.getTraceId(), endpoint_execution_span.getSpanId(), endpoint_execution_span.getParentSpanId(), String.valueOf(endpoint_execution_span.isSampleable()), endpoint_execution_span.getUserId() ); }
@Test public void fromHttpServletRequest_returns_null_if_passed_null_request() { // when Span nullSpan = HttpSpanFactory.fromHttpServletRequest(null, USER_ID_HEADER_KEYS); // then //noinspection ConstantConditions assertThat(nullSpan).isNull(); }
@Test public void getSpanName_returns_UNKNOWN_HTTP_METHOD_when_passed_null_request() { // expect assertThat(HttpSpanFactory.getSpanName(null)).isEqualTo("UNKNOWN_HTTP_METHOD"); } }
@Test public void fromHttpServletRequestOrCreateRootSpan_returns_new_root_span_if_traceId_is_missing_from_headers() { // given: no trace ID in headers, but user ID exists given(request.getHeader(ALT_USER_ID_HEADER_KEY)).willReturn(altUserId); // when: creating Span object from HTTP request using fromHttpServletRequestOrCreateRootSpan long beforeCallNanos = System.nanoTime(); Span newSpan = HttpSpanFactory.fromHttpServletRequestOrCreateRootSpan(request, USER_ID_HEADER_KEYS); long afterCallNanos = System.nanoTime(); // then: ensure root span object is created even though there was no trace ID header, and the returned span contains the expected user ID assertThat(newSpan).isNotNull(); assertThat(newSpan.getParentSpanId()).isNull(); assertThat(newSpan.getUserId()).isEqualTo(altUserId); assertThat(newSpan.getSpanStartTimeNanos()).isBetween(beforeCallNanos, afterCallNanos); assertThat(newSpan.isCompleted()).isFalse(); assertThat(newSpan.getSpanPurpose()).isEqualTo(SpanPurpose.SERVER); }
public EndpointSpanInfoDto(HttpServletRequest request, Span endpoint_execution_span, List<String> userIdHeaderKeys) { this.parent_span_info = new SpanInfoDto( request.getHeader(TraceHeaders.TRACE_ID), request.getHeader(TraceHeaders.SPAN_ID), request.getHeader(TraceHeaders.PARENT_SPAN_ID), request.getHeader(TraceHeaders.TRACE_SAMPLED), HttpSpanFactory.getUserIdFromHttpServletRequest(request, userIdHeaderKeys) ); this.endpoint_execution_span_info = new SpanInfoDto( endpoint_execution_span.getTraceId(), endpoint_execution_span.getSpanId(), endpoint_execution_span.getParentSpanId(), String.valueOf(endpoint_execution_span.isSampleable()), endpoint_execution_span.getUserId() ); }
@Test public void fromHttpServletRequest_generates_new_spanId_if_missing_from_headers() { // given: a request with a trace ID but no span ID in the headers String traceId = UUID.randomUUID().toString(); given(request.getHeader(TraceHeaders.TRACE_ID)).willReturn(traceId); // when: we use it to create span objects Span firstSpan = HttpSpanFactory.fromHttpServletRequest(request, USER_ID_HEADER_KEYS); Span secondSpan = HttpSpanFactory.fromHttpServletRequest(request, USER_ID_HEADER_KEYS); // then: ensure each call generates a span with the same trace ID but new span ID assertThat(firstSpan.getTraceId()).isEqualTo(traceId); assertThat(secondSpan.getTraceId()).isEqualTo(traceId); assertThat(firstSpan.getSpanId()).isNotEmpty(); assertThat(secondSpan.getSpanId()).isNotEmpty(); assertThat(firstSpan.getSpanId()).isNotEqualTo(secondSpan.getSpanId()); }
/** * @param request The incoming request. * @param namingStrategy The {@link HttpTagAndSpanNamingStrategy} that should be used to try and generate the * initial span name - cannot be null. * @param adapter The {@link HttpTagAndSpanNamingAdapter} that should be passed to the given {@code namingStrategy} * to try and generate the initial span name - cannot be null. * @return The human-readable name to be given to a {@link Span} representing this request. By default this method * attempts to use {@link HttpTagAndSpanNamingStrategy#getInitialSpanName(Object, HttpTagAndSpanNamingAdapter)} * with the given {@code namingStrategy} and {@code adapter} for generating the name, and falls back to * {@link HttpSpanFactory#getSpanName(HttpServletRequest)} if the {@link HttpTagAndSpanNamingStrategy} returns * null or blank. */ protected String getInitialSpanName( HttpServletRequest request, HttpTagAndSpanNamingStrategy<HttpServletRequest, ?> namingStrategy, HttpTagAndSpanNamingAdapter<HttpServletRequest, ?> adapter ) { // Try the naming strategy first. String spanNameFromStrategy = namingStrategy.getInitialSpanName(request, adapter); if (StringUtils.isNotBlank(spanNameFromStrategy)) { return spanNameFromStrategy; } // The naming strategy didn't have anything for us. Fall back to something reasonable. return HttpSpanFactory.getSpanName(request); }
@Test public void fromHttpServletRequestOrCreateRootSpan_pulls_from_headers_if_available() { // given: a set of standard Span header values given(request.getHeader(TraceHeaders.TRACE_ID)).willReturn(sampleTraceID); given(request.getHeader(TraceHeaders.TRACE_SAMPLED)).willReturn(Boolean.TRUE.toString()); given(request.getHeader(TraceHeaders.SPAN_ID)).willReturn(sampleSpanID); given(request.getHeader(TraceHeaders.PARENT_SPAN_ID)).willReturn(sampleParentSpanID); given(request.getHeader(USER_ID_HEADER_KEY)).willReturn(userId); // when: creating Span object from HTTP request using fromHttpServletRequestOrCreateRootSpan long beforeCallNanos = System.nanoTime(); Span goodSpan = HttpSpanFactory.fromHttpServletRequestOrCreateRootSpan(request, USER_ID_HEADER_KEYS); long afterCallNanos = System.nanoTime(); // then: ensure Span object gets identical values from corresponding headers assertThat(goodSpan.getTraceId()).isEqualTo(sampleTraceID); assertThat(goodSpan.isSampleable()).isTrue(); assertThat(goodSpan.getSpanId()).isEqualTo(sampleSpanID); assertThat(goodSpan.getParentSpanId()).isEqualTo(sampleParentSpanID); assertThat(goodSpan.getUserId()).isEqualTo(userId); assertThat(goodSpan.getSpanStartTimeNanos()).isBetween(beforeCallNanos, afterCallNanos); assertThat(goodSpan.isCompleted()).isFalse(); assertThat(goodSpan.getSpanPurpose()).isEqualTo(SpanPurpose.SERVER); }
@Test public void getUserIdFromHttpServletRequest_returns_null_if_passed_null_request() { // when String nullUserId = HttpSpanFactory.getUserIdFromHttpServletRequest(null, USER_ID_HEADER_KEYS); // then //noinspection ConstantConditions assertThat(nullUserId).isNull(); }
@Test public void fromHttpServletRequest_sets_span_sampleable_to_false_if_trace_sampled_header_is_null_but_attribute_returns_false() { // given: request where the header for TRACE_SAMPLED returns null but the attribute returns false given(request.getHeader(TraceHeaders.TRACE_ID)).willReturn(sampleTraceID); given(request.getHeader(TraceHeaders.TRACE_SAMPLED)).willReturn(null); given(request.getAttribute(TraceHeaders.TRACE_SAMPLED)).willReturn(false); // when: creating span from request Span newSpan = HttpSpanFactory.fromHttpServletRequest(request, USER_ID_HEADER_KEYS); // then: new span should be disabled assertThat(newSpan.isSampleable()).isFalse(); }
/** * Generates a span name appropriate for a new root span for this request. * * <p>NOTE: Due to problems that can crop up in visualization and analysis systems when there is high cardinality on * span names, this method does its best to discover the "path template" and use that in the span name rather than * raw full URI path. i.e. We try to return {@code /foo/:id} (or however your framework shows path template) rather * than {@code /foo/12345}. We use {@link #determineUriPathTemplate(HttpServletRequest)} for this purpose - see * the javadocs for that method for the rules that are used. * * <p>All returned span names will start with the {@link HttpServletRequest#getMethod()} for the request, but if * we can't discover the path template, then the HTTP method is all that the span name will contain. * * @return A Span name appropriate for a new root span for this request. */ public static @NotNull String getSpanName(@Nullable HttpServletRequest request) { /* NOTE: We used to use request.getServletPath() and request.getRequestURI() as last resorts, but that leads to high cardinality span names (even with request.getServletPath(), thanks to different frameworks not really honoring the contract for that method), which is really bad for visualization systems that try to do any kind of analytics on your spans. Now that we have span tags and are auto-tagging spans with the full path, we won't be missing data if we drop the full path from the span name. So if we don't have a path at this point then we'll do what Zipkin does and just default to the HTTP method. */ String pathTemplate = determineUriPathTemplate(request); String method = (request == null) ? null : request.getMethod(); // HttpRequestTracingUtils.generateSafeSpanName() gives us what we want, and properly handles the case // where everything passed into it is null. return HttpRequestTracingUtils.generateSafeSpanName(method, pathTemplate, (Integer)null); }
String result = HttpSpanFactory.getSpanName(request);
@Test public void fromHttpServletRequest_defaults_span_sampleable_value_to_true_if_request_header_and_attribute_is_null() { // given: request where the request header and attribute for TRACE_SAMPLED returns null given(request.getHeader(TraceHeaders.TRACE_ID)).willReturn(sampleTraceID); given(request.getHeader(TraceHeaders.TRACE_SAMPLED)).willReturn(null); given(request.getAttribute(TraceHeaders.TRACE_SAMPLED)).willReturn(null); // when: creating span from request Span newSpan = HttpSpanFactory.fromHttpServletRequest(request, USER_ID_HEADER_KEYS); // then: new span should be sampleable assertThat(newSpan.isSampleable()).isTrue(); }
@Test public void determineUriPathTemplate_returns_null_if_passed_null() { // expect assertThat(HttpSpanFactory.determineUriPathTemplate(null)).isNull(); }
@Test public void fromHttpServletRequest_sets_span_sampleable_to_false_if_trace_sampled_header_is_empty_but_attribute_returns_false() { // given: request where the header for TRACE_SAMPLED returns null but the attribute returns false given(request.getHeader(TraceHeaders.TRACE_ID)).willReturn(sampleTraceID); given(request.getHeader(TraceHeaders.TRACE_SAMPLED)).willReturn(" "); given(request.getAttribute(TraceHeaders.TRACE_SAMPLED)).willReturn(false); // when: creating span from request Span newSpan = HttpSpanFactory.fromHttpServletRequest(request, USER_ID_HEADER_KEYS); // then: new span should be disabled assertThat(newSpan.isSampleable()).isFalse(); }
String result = HttpSpanFactory.determineUriPathTemplate(request);