diff --git a/multi/multi__current_span.html b/multi/multi__current_span.html new file mode 100644 index 000000000..9b52c3f27 --- /dev/null +++ b/multi/multi__current_span.html @@ -0,0 +1,20 @@ +
+ +Brave supports a "current span" concept which represents the in-flight
+operation. Tracer.currentSpan() can be used to add custom tags to a
+span and Tracer.nextSpan() can be used to create a child of whatever
+is in-flight.
When writing new instrumentation, it is important to place a span you
+created in scope as the current span. Not only does this allow users to
+access it with Tracer.currentSpan(), but it also allows customizations
+like SLF4J MDC to see the current trace IDs.
Tracer.withSpanInScope(Span) facilitates this and is most conveniently
+employed via the try-with-resources idiom. Whenever external code might
+be invoked (such as proceeding an interceptor or otherwise), place the
+span in scope like this.
try (SpanInScope ws = tracer.withSpanInScope(span)) { + return inboundRequest.invoke(); +} finally { // note the scope is independent of the span + span.finish(); +}
In edge cases, you may need to clear the current span temporarily. For
+example, launching a task that should not be associated with the current
+request. To do this, simply pass null to withSpanInScope.
try (SpanInScope cleared = tracer.withSpanInScope(null)) {
+ startBackgroundThread();
+}Brave supports a "current tracing component" concept which should only +be used when you have no other means to get a reference. This was made +for JDBC connections, as they often initialize prior to the tracing +component.
The most recent tracing component instantiated is available via
+Tracing.current(). You there’s also a shortcut to get only the tracer
+via Tracing.currentTracer(). If you use either of these methods, do
+noot cache the result. Instead, look them up each time you need them.
Thanks to the SpanInjector and SpanExtractor you can customize the way spans
-are created and propagated.
There are currently two built-in ways to pass tracing information between processes:
Span ids are extracted from Zipkin-compatible (B3) headers (either Message
-or HTTP headers), to start or join an existing trace. Trace information is
-injected into any outbound requests so the next hop can extract them.
The key change in comparison to the previous versions of Sleuth is that Sleuth is implementing
-the Open Tracing’s TextMap notion. In Sleuth it’s called SpanTextMap. Basically the idea
-is that any means of communication (e.g. message, http request, etc.) can be abstracted via
-a SpanTextMap. This abstraction defines how one can insert data into the carrier and
-how to retrieve it from there. Thanks to this if you want to instrument a new HTTP library
-that uses a FooRequest as a mean of sending HTTP requests then you have to create an
-implementation of a SpanTextMap that delegates calls to FooRequest in terms of retrieval
-and insertion of HTTP headers.
For Spring Integration there are 2 interfaces responsible for creation of a Span from a Message.
-These are:
MessagingSpanTextMapExtractorMessagingSpanTextMapInjectorYou can override them by providing your own implementation.
For HTTP there are 2 interfaces responsible for creation of a Span from a Message.
-These are:
HttpSpanExtractorHttpSpanInjectorYou can override them by providing your own implementation.
Let’s assume that instead of the standard Zipkin compatible tracing HTTP header names -you have
correlationIdmySpanIdThis is a an example of a SpanExtractor
static class CustomHttpSpanExtractor implements HttpSpanExtractor { - - @Override public Span joinTrace(SpanTextMap carrier) { - Map<String, String> map = TextMapUtil.asMap(carrier); - long traceId = Span.hexToId(map.get("correlationid")); - long spanId = Span.hexToId(map.get("myspanid")); - // extract all necessary headers - Span.SpanBuilder builder = Span.builder().traceId(traceId).spanId(spanId); - // build rest of the Span - return builder.build(); - } -} - -static class CustomHttpSpanInjector implements HttpSpanInjector { - - @Override - public void inject(Span span, SpanTextMap carrier) { - carrier.put("correlationId", span.traceIdString()); - carrier.put("mySpanId", Span.idToHex(span.getSpanId())); - } -}
And you could register it like this:
@Bean -HttpSpanInjector customHttpSpanInjector() { - return new CustomHttpSpanInjector(); -} - -@Bean -HttpSpanExtractor customHttpSpanExtractor() { - return new CustomHttpSpanExtractor(); -}
Spring Cloud Sleuth does not add trace/span related headers to the Http Response for security reasons. If you need the headers then a custom SpanInjector
-that injects the headers into the Http Response and a Servlet filter which makes use of this can be added the following way:
static class CustomHttpServletResponseSpanInjector extends ZipkinHttpSpanInjector { - - @Override - public void inject(Span span, SpanTextMap carrier) { - super.inject(span, carrier); - carrier.put(Span.TRACE_ID_NAME, span.traceIdString()); - carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); - } -} - -static class HttpResponseInjectingTraceFilter extends GenericFilterBean { - - private final Tracer tracer; - private final HttpSpanInjector spanInjector; - - public HttpResponseInjectingTraceFilter(Tracer tracer, HttpSpanInjector spanInjector) { - this.tracer = tracer; - this.spanInjector = spanInjector; - } - - @Override - public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - HttpServletResponse response = (HttpServletResponse) servletResponse; - Span currentSpan = this.tracer.getCurrentSpan(); - this.spanInjector.inject(currentSpan, new HttpServletResponseTextMap(response)); - filterChain.doFilter(request, response); - } - - class HttpServletResponseTextMap implements SpanTextMap { - - private final HttpServletResponse delegate; - - HttpServletResponseTextMap(HttpServletResponse delegate) { - this.delegate = delegate; - } - - @Override - public Iterator<Map.Entry<String, String>> iterator() { - Map<String, String> map = new HashMap<>(); - for (String header : this.delegate.getHeaderNames()) { - map.put(header, this.delegate.getHeader(header)); - } - return map.entrySet().iterator(); - } - - @Override - public void put(String key, String value) { - this.delegate.addHeader(key, value); - } - } -}
And you could register them like this:
@Bean HttpSpanInjector customHttpServletResponseSpanInjector() { - return new CustomHttpServletResponseSpanInjector(); -} - -@Bean -HttpResponseInjectingTraceFilter responseInjectingTraceFilter(Tracer tracer) { - return new HttpResponseInjectingTraceFilter(tracer, customHttpServletResponseSpanInjector()); -}
You can also modify the behaviour of the TraceFilter - the component that is responsible
+
You can also modify the behaviour of the TraceFilter - the component that is responsible
for processing the input HTTP request and adding tags basing on the HTTP response. You can customize
the tags, or modify the response headers by registering your own instance of the TraceFilter bean.
In the following example we will register the TraceFilter bean and we will add the
ZIPKIN-TRACE-ID response header containing the current Span’s trace id. Also we will
-add to the Span a tag with key custom and a value tag.
@Bean -TraceFilter myTraceFilter(BeanFactory beanFactory, final Tracer tracer) { - return new TraceFilter(beanFactory) { - @Override protected void addResponseTags(HttpServletResponse response, - Throwable e) { - // execute the default behaviour - super.addResponseTags(response, e); - // for readability we're returning trace id in a hex form - response.addHeader("ZIPKIN-TRACE-ID", - Span.idToHex(tracer.getCurrentSpan().getTraceId())); - // we can also add some custom tags - tracer.addTag("custom", "tag"); - } - }; -}
Sometimes you want to create a manual Span that will wrap a call to an external service which is not instrumented.
-What you can do is to create a span with the peer.service tag that will contain a value of the service that you want to call.
-Below you can see an example of a call to Redis that is wrapped in such a span.
Span span = tracer.createSpan("redis"); -try { - span.tag("redis.op", "get"); - span.tag("lc", "redis"); - span.logEvent("cs"); - // call redis service e.g - // return (SomeObj) redisTemplate.opsForHash().get("MYHASH", someObjKey); -} finally { - span.tag("peer.service", "redis"); - span.tag("peer.ipv4", "1.2.3.4"); - span.tag("peer.port", "1234"); - span.logEvent("cr"); - span.stop(); -}
![]() | Important |
|---|---|
Remember not to add both |
By default Sleuth assumes that when you send a span to Zipkin, you want the span’s service name
+add to the Span a tag with key custom and a value tag.
@Component +@Order(TraceFilter.ORDER + 1) +class MyFilter extends GenericFilterBean { + + private final Tracer tracer; + + MyFilter(Tracer tracer) { + this.tracer = tracer; + } + + @Override public void doFilter(ServletRequest request, ServletResponse response, + FilterChain chain) throws IOException, ServletException { + Span currentSpan = this.tracer.currentSpan(); + then(currentSpan).isNotNull(); + // for readability we're returning trace id in a hex form + ((HttpServletResponse) response) + .addHeader("ZIPKIN-TRACE-ID", + currentSpan.context().traceIdString()); + // we can also add some custom tags + currentSpan.tag("custom", "tag"); + chain.doFilter(request, response); + } +}
By default Sleuth assumes that when you send a span to Zipkin, you want the span’s service name
to be equal to spring.application.name value. That’s not always the case though. There
are situations in which you want to explicitly provide a different service name for all spans coming
from your application. To achieve that it’s enough to just pass the following property
- to your application to override that value (example for foo service name):
spring.zipkin.service.name: fooBefore reporting spans to e.g. Zipkin you can be interested in modifying that span in some way.
- You can achieve that by using the SpanAdjuster interface.
Example of usage:
In Sleuth we’re generating spans with a fixed name. Some users want to modify the name depending on values
-of tags. Implementation of the SpanAdjuster interface can be used to alter that name. Example:
@Bean -SpanAdjuster customSpanAdjuster() { - return span -> span.toBuilder().name(scrub(span.getName())).build(); -}
This will lead in changing the name of the reported span just before it gets sent to Zipkin.
![]() | Important |
|---|---|
Your |
In order to define the host that is corresponding to a particular span we need to resolve the host name
+ to your application to override that value (example for foo service name):
spring.zipkin.service.name: fooBefore reporting spans to e.g. Zipkin you can be interested in modifying that span in some way.
+ You can achieve that by using the SpanAdjuster interface.
In Sleuth we’re generating spans with a fixed name. Some users want to modify the name depending on values
+of tags. Implementation of the SpanAdjuster interface can be used to alter that name. Example:
Example. If you register two beans of SpanAdjuster type:
Unresolved directive in spring-cloud-sleuth.adoc - include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/autoconfig/SpanAdjusterAspectTests.java[tags=adjuster,indent=0]This will lead in changing the name of the reported span to foo bar, just before it gets reported (e.g. to Zipkin).
![]() | Important |
|---|---|
This section is about defining host from service discovery. It’s NOT +about finding Zipkin in service discovery. |
In order to define the host that is corresponding to a particular span we need to resolve the host name and port. The default approach is to take it from server properties. If those for some reason are not set then we’re trying to retrieve the host name from the network interfaces.
If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry then you have to set the property (it’s applicable for both HTTP and -Stream based span reporting).
spring.zipkin.locator.discovery.enabled: true
spring.zipkin.locator.discovery.enabled: true