Sleuth now uses Brave (#829)
with this pull request we have rewritten the whole Sleuth internals to use Brave. That way we can leverage all the functionalities & instrumentations that Brave already has (https://github.com/openzipkin/brave/tree/master/instrumentation). Migration guide is available here: https://github.com/spring-cloud/spring-cloud-sleuth/wiki/Spring-Cloud-Sleuth-2.0-Migration-Guide fixes #711 - Brave instrumentation fixes #92 - we move to Brave's Sampler fixes #143 - Brave is capable of passing context fixes #255 - we've moved away from Zipkin Stream server fixes #305 - Brave has GRPC instrumentation (https://github.com/openzipkin/brave/tree/master/instrumentation/grpc) fixes #459 - Brave (openzipkin/brave#510) & Zipkin (openzipkin/zipkin#1754) will deal with the AWS XRay instrumentation fixes #577 - Messaging instrumentation has been rewritten
This commit is contained in:
committed by
GitHub
parent
9f716d7d92
commit
7eb374b5a5
@@ -6,7 +6,7 @@
|
||||
:github-code: https://github.com/{github-repo}/tree/{github-tag}
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/{github-tag}/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://badges.gitter.im/spring-cloud/spring-cloud-sleuth.svg[Gitter, link="https://gitter.im/spring-cloud/spring-cloud-sleuth?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
|
||||
== Spring Cloud Sleuth
|
||||
|
||||
|
||||
@@ -22,8 +22,12 @@ of that span is equal to trace id.
|
||||
*Trace:* A set of spans forming a tree-like structure. For example, if you are running a distributed
|
||||
big-data store, a trace might be formed by a put request.
|
||||
|
||||
*Annotation:* is used to record existence of an event in time. Some of the core annotations used to define
|
||||
the start and stop of a request are:
|
||||
*Annotation:* is used to record existence of an event in time. With
|
||||
https://github.com/openzipkin/brave[Brave] instrumentation we no longer need to set special events
|
||||
for https://zipkin.io/[Zipkin] to understand who the client and server are and where
|
||||
the request started and where it has ended. For learning purposes
|
||||
however we will mark these events to highlight what kind
|
||||
of an action took place.
|
||||
|
||||
- *cs* - Client Sent - The client has made a request. This annotation depicts the start of the span.
|
||||
- *sr* - Server Received - The server side got the request and will start processing it.
|
||||
@@ -46,8 +50,8 @@ Trace Id = X
|
||||
Span Id = D
|
||||
Client Sent
|
||||
|
||||
That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*. It also has emitted
|
||||
*Client Sent* event.
|
||||
That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*. Also, the
|
||||
*Client Sent* event took place.
|
||||
|
||||
This is how the visualization of the parent / child relationship of spans would look like:
|
||||
|
||||
@@ -74,14 +78,14 @@ annotations then they will presented as a single span.
|
||||
Why is there a difference between the 7 and 4 spans in this case?
|
||||
|
||||
- 2 spans come from `http:/start` span. It has the Server Received (SR) and Server Sent (SS) annotations.
|
||||
- 2 spans come from the RPC call from `service1` to `service2` to the `http:/foo` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service1` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service1` to `service2` to the `http:/foo` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service1` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service2` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
- 2 spans come from the RPC call from `service2` to `service3` to the `http:/bar` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service2` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service2` to `service3` to the `http:/bar` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service2` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service3` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
- 2 spans come from the RPC call from `service2` to `service4` to the `http:/baz` endpoint. It has the Client Sent (CS)
|
||||
and Client Received (CR) annotations on `service2` side. It also has Server Received (SR) and Server Sent (SS) annotations
|
||||
- 2 spans come from the RPC call from `service2` to `service4` to the `http:/baz` endpoint. The Client Sent (CS)
|
||||
and Client Received (CR) events took place on `service2` side. Server Received (SR) and Server Sent (SS) events took place
|
||||
on the `service4` side. Physically there are 2 spans but they form 1 logical span related to an RPC call.
|
||||
|
||||
So if we count the physical spans we have *1* from `http:/start`, *2* from `service1` calling `service2`, *2* form `service2`
|
||||
@@ -106,11 +110,25 @@ image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branc
|
||||
|
||||
As you can see you can easily see the reason for an error and the whole stacktrace related to it.
|
||||
|
||||
==== Distributed tracing with Brave
|
||||
|
||||
Starting with version `2.0.0`, Spring Cloud Sleuth uses
|
||||
https://github.com/openzipkin/brave[Brave] as the tracing library. That means
|
||||
that Sleuth no longer takes care of storing the context but it delegates
|
||||
that work to Brave.
|
||||
|
||||
Due to the fact that Sleuth had different naming / tagging
|
||||
conventions than Brave, we've decided to follow the Brave's
|
||||
conventions from now on. However, if you want to use the legacy
|
||||
Sleuth approaches, it's enough to set the `spring.sleuth.http.legacy.enabled` property
|
||||
to `true`.
|
||||
|
||||
==== Live examples
|
||||
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branch}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/", width=150, height=74]
|
||||
http://docssleuth-zipkin-server.cfapps.io/[Click here to see it live!]
|
||||
|
||||
The dependency graph in Zipkin would look like this:
|
||||
|
||||
@@ -119,7 +137,7 @@ image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branc
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/{branch}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/dependency", width=150, height=74]
|
||||
|
||||
http://docssleuth-zipkin-server.cfapps.io/dependency[Click here to see it live!]
|
||||
|
||||
==== Log correlation
|
||||
|
||||
@@ -203,7 +221,8 @@ Example of setting baggage on a span:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{github-raw}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
|
||||
include::{github-raw}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/brave/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
|
||||
}
|
||||
----
|
||||
|
||||
===== Baggage vs. Span Tags
|
||||
@@ -216,15 +235,11 @@ can search by tag to find the trace, where there exists a span having the search
|
||||
|
||||
If you want to be able to lookup a span based on baggage, you should add corresponding entry as a tag in the root span.
|
||||
|
||||
IMPORTANT: Remember that the span needs to be in scope!
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
Span span = tracer.getCurrentSpan();
|
||||
String baggageKey = "key";
|
||||
String baggageValue = "foo";
|
||||
span.setBaggageItem(baggageKey, baggageValue);
|
||||
tracer.addTag(baggageKey, baggageValue);
|
||||
include::{github-raw}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/brave/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage_tag,indent=0]
|
||||
----
|
||||
|
||||
=== Adding to the project
|
||||
@@ -240,7 +255,7 @@ the `spring-cloud-starter-sleuth` module to your project.
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -285,7 +300,7 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin` de
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
@@ -336,7 +351,7 @@ dependencies.
|
||||
[source,xml,indent=0,subs="verbatim,attributes",role="primary"]
|
||||
.Maven
|
||||
----
|
||||
<dependencyManagement> <1>
|
||||
<dependencyManagement> <1>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
|
||||
@@ -19,29 +19,287 @@ include::intro.adoc[]
|
||||
|
||||
include::features.adoc[]
|
||||
|
||||
=== Introduction to Brave
|
||||
|
||||
IMPORTANT: Starting with version `2.0.0` Spring Cloud Sleuth uses
|
||||
https://github.com/openzipkin/brave[Brave] as the tracing library.
|
||||
For your convenience we're embedding part of the Brave's docs here.
|
||||
|
||||
Brave is a library used to capture and report latency information about
|
||||
distributed operations to Zipkin. Most users won't use Brave directly,
|
||||
rather libraries or frameworks than employ Brave on their behalf.
|
||||
|
||||
This module includes tracer creates and joins spans that model the
|
||||
latency of potentially distributed work. It also includes libraries to
|
||||
propagate the trace context over network boundaries, for example, via
|
||||
http headers.
|
||||
|
||||
==== Tracing
|
||||
|
||||
Most importantly, you need a `brave.Tracer`, configured to [report to Zipkin]
|
||||
(https://github.com/openzipkin/zipkin-reporter-java).
|
||||
|
||||
Here's an example setup that sends trace data (spans) to Zipkin over
|
||||
http (as opposed to Kafka).
|
||||
|
||||
```java
|
||||
|
||||
class MyClass {
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
// Tracer will be autowired
|
||||
MyClass(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
void doSth() {
|
||||
Span span = tracer.newTrace().name("encode").start();
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
IMPORTANT: If your span contains a name greater than 50 chars, then that name will
|
||||
be truncated to 50 chars. Your names have to be explicit and concrete. Big names lead to
|
||||
latency issues and sometimes even thrown exceptions.
|
||||
|
||||
==== Tracing
|
||||
|
||||
The tracer creates and joins spans that model the latency of potentially
|
||||
distributed work. It can employ sampling to reduce overhead in process
|
||||
or to reduce the amount of data sent to Zipkin.
|
||||
|
||||
Spans returned by a tracer report data to Zipkin when finished, or do
|
||||
nothing if unsampled. After starting a span, you can annotate events of
|
||||
interest or add tags containing details or lookup keys.
|
||||
|
||||
Spans have a context which includes trace identifiers that place it at
|
||||
the correct spot in the tree representing the distributed operation.
|
||||
|
||||
==== Local Tracing
|
||||
|
||||
When tracing local code, just run it inside a span.
|
||||
|
||||
```java
|
||||
Span span = tracer.newTrace().name("encode").start();
|
||||
try {
|
||||
doSomethingExpensive();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the span is the root of the trace. In many cases,
|
||||
you will be a part of an existing trace. When this is the case, call
|
||||
`newChild` instead of `newTrace`
|
||||
|
||||
```java
|
||||
Span span = tracer.newChild(root.context()).name("encode").start();
|
||||
try {
|
||||
doSomethingExpensive();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
```
|
||||
|
||||
==== Customizing spans
|
||||
|
||||
Once you have a span, you can add tags to it, which can be used as lookup
|
||||
keys or details. For example, you might add a tag with your runtime
|
||||
version.
|
||||
|
||||
```java
|
||||
span.tag("clnt/finagle.version", "6.36.0");
|
||||
```
|
||||
|
||||
When exposing the ability to customize spans to third parties, prefer
|
||||
`brave.SpanCustomizer` as opposed to `brave.Span`. The former is simpler to
|
||||
understand and test, and doesn't tempt users with span lifecycle hooks.
|
||||
|
||||
```java
|
||||
interface MyTraceCallback {
|
||||
void request(Request request, SpanCustomizer customizer);
|
||||
}
|
||||
```
|
||||
|
||||
Since `brave.Span` implements `brave.SpanCustomizer`, it is just as easy for you
|
||||
to pass to users.
|
||||
|
||||
Ex.
|
||||
```java
|
||||
for (MyTraceCallback callback : userCallbacks) {
|
||||
callback.request(request, span);
|
||||
}
|
||||
```
|
||||
|
||||
==== Implicitly looking up the current span
|
||||
|
||||
Sometimes you won't know if a trace is in progress or not, and you don't
|
||||
want users to do null checks. `brave.CurrentSpanCustomizer` adds to any
|
||||
span that's in progress or drops data accordingly.
|
||||
|
||||
Ex.
|
||||
```java
|
||||
// user code can then inject this without a chance of it being null.
|
||||
@Autowire SpanCustomizer span;
|
||||
|
||||
void userCode() {
|
||||
span.annotate("tx.started");
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
==== RPC tracing
|
||||
|
||||
Check for https://github.com/openzipkin/sleuth/tree/master/instrumentation[instrumentation written here]
|
||||
and http://zipkin.io/pages/existing_instrumentations.html[Zipkin's list]
|
||||
before rolling your own RPC instrumentation!
|
||||
|
||||
RPC tracing is often done automatically by interceptors. Under the scenes,
|
||||
they add tags and events that relate to their role in an RPC operation.
|
||||
|
||||
Here's an example of a client span:
|
||||
|
||||
```java
|
||||
// before you send a request, add metadata that describes the operation
|
||||
span = tracer.newTrace().name("get").type(CLIENT);
|
||||
span.tag("clnt/finagle.version", "6.36.0");
|
||||
span.tag(TraceKeys.HTTP_PATH, "/api");
|
||||
span.remoteEndpoint(Endpoint.builder()
|
||||
.serviceName("backend")
|
||||
.ipv4(127 << 24 | 1)
|
||||
.port(8080).build());
|
||||
|
||||
// when the request is scheduled, start the span
|
||||
span.start();
|
||||
|
||||
// if you have callbacks for when data is on the wire, note those events
|
||||
span.annotate(Constants.WIRE_SEND);
|
||||
span.annotate(Constants.WIRE_RECV);
|
||||
|
||||
// when the response is complete, finish the span
|
||||
span.finish();
|
||||
```
|
||||
|
||||
===== One-Way tracing
|
||||
|
||||
Sometimes you need to model an asynchronous operation, where there is a
|
||||
request, but no response. In normal RPC tracing, you use `span.finish()`
|
||||
which indicates the response was received. In one-way tracing, you use
|
||||
`span.flush()` instead, as you don't expect a response.
|
||||
|
||||
Here's how a client might model a one-way operation
|
||||
```java
|
||||
// start a new span representing a client request
|
||||
oneWaySend = tracer.newSpan(parent).kind(Span.Kind.CLIENT);
|
||||
|
||||
// Add the trace context to the request, so it can be propagated in-band
|
||||
tracing.propagation().injector(Request::addHeader)
|
||||
.inject(oneWaySend.context(), request);
|
||||
|
||||
// fire off the request asynchronously, totally dropping any response
|
||||
request.execute();
|
||||
|
||||
// start the client side and flush instead of finish
|
||||
oneWaySend.start().flush();
|
||||
```
|
||||
|
||||
And here's how a server might handle this..
|
||||
```java
|
||||
// pull the context out of the incoming request
|
||||
extractor = tracing.propagation().extractor(Request::getHeader);
|
||||
|
||||
// convert that context to a span which you can name and add tags to
|
||||
oneWayReceive = nextSpan(tracer, extractor.extract(request))
|
||||
.name("process-request")
|
||||
.kind(SERVER)
|
||||
... add tags etc.
|
||||
|
||||
// start the server side and flush instead of finish
|
||||
oneWayReceive.start().flush();
|
||||
|
||||
// you should not modify this span anymore as it is complete. However,
|
||||
// you can create children to represent follow-up work.
|
||||
next = tracer.newSpan(oneWayReceive.context()).name("step2").start();
|
||||
```
|
||||
|
||||
**Note** The above propagation logic is a simplified version of our [http handlers](https://github.com/openzipkin/sleuth/tree/master/instrumentation/http#http-server).
|
||||
|
||||
There's a working example of a one-way span [here](src/test/java/sleuth/features/async/OneWaySpanTest.java).
|
||||
|
||||
== Sampling
|
||||
|
||||
In distributed tracing the data volumes can be very high so sampling
|
||||
can be important (you usually don't need to export all spans to get a
|
||||
good picture of what is happening). Spring Cloud Sleuth has a
|
||||
`Sampler` strategy that you can implement to take control of the
|
||||
sampling algorithm. Samplers do not stop span (correlation) ids from
|
||||
being generated, but they do prevent the tags and events being
|
||||
attached and exported. By default you get a strategy that continues to
|
||||
trace if a span is already active, but new ones are always marked as
|
||||
non-exportable. If all your apps run with this sampler you will see
|
||||
traces in logs, but not in any remote store. For testing the default
|
||||
is often enough, and it probably is all you need if you are only using
|
||||
the logs (e.g. with an ELK aggregator). If you are exporting span data
|
||||
to Zipkin or Spring Cloud Stream, there is also an `AlwaysSampler`
|
||||
that exports everything and a `PercentageBasedSampler` that samples a
|
||||
Sampling may be employed to reduce the data collected and reported out
|
||||
of process. When a span isn't sampled, it adds no overhead (noop).
|
||||
|
||||
Sampling is an up-front decision, meaning that the decision to report
|
||||
data is made at the first operation in a trace, and that decision is
|
||||
propagated downstream.
|
||||
|
||||
By default, there's a global sampler that applies a single rate to all
|
||||
traced operations. `Tracer.Builder.sampler` is how you indicate this,
|
||||
and it defaults to trace every request.
|
||||
|
||||
=== Declarative sampling
|
||||
|
||||
Some need to sample based on the type or annotations of a java method.
|
||||
|
||||
Most users will use a framework interceptor which automates this sort of
|
||||
policy. Here's how they might work internally.
|
||||
|
||||
```java
|
||||
// derives a sample rate from an annotation on a java method
|
||||
DeclarativeSampler<Traced> sampler = DeclarativeSampler.create(Traced::sampleRate);
|
||||
|
||||
@Around("@annotation(traced)")
|
||||
public Object traceThing(ProceedingJoinPoint pjp, Traced traced) throws Throwable {
|
||||
Span span = tracing.tracer().newTrace(sampler.sample(traced))...
|
||||
try {
|
||||
return pjp.proceed();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
=== Custom sampling
|
||||
|
||||
You may want to apply different policies depending on what the operation
|
||||
is. For example, you might not want to trace requests to static resources
|
||||
such as images, or you might want to trace all requests to a new api.
|
||||
|
||||
Most users will use a framework interceptor which automates this sort of
|
||||
policy. Here's how they might work internally.
|
||||
|
||||
```java
|
||||
Span newTrace(Request input) {
|
||||
SamplingFlags flags = SamplingFlags.NONE;
|
||||
if (input.url().startsWith("/experimental")) {
|
||||
flags = SamplingFlags.SAMPLED;
|
||||
} else if (input.url().startsWith("/static")) {
|
||||
flags = SamplingFlags.NOT_SAMPLED;
|
||||
}
|
||||
return tracer.newTrace(flags);
|
||||
}
|
||||
```
|
||||
|
||||
Note: the above is the basis for the built-in https://github.com/openzipkin/sleuth/tree/master/instrumentation/http[http sampler]
|
||||
|
||||
=== Sampling in Spring Cloud Sleuth
|
||||
|
||||
Spring Cloud Sleuth by default sets all spans to non-exportable.
|
||||
That means that you will see traces in logs, but not in any remote store.
|
||||
For testing the default is often enough, and it probably is all you need
|
||||
if you are only using the logs (e.g. with an ELK aggregator). If you are
|
||||
exporting span data to Zipkin, there is also an `Sampler.ALWAYS_SAMPLE`
|
||||
that exports everything and a `ProbabilityBasedSampler` that samples a
|
||||
fixed fraction of spans.
|
||||
|
||||
NOTE: the `PercentageBasedSampler` is the default if you are using
|
||||
`spring-cloud-sleuth-zipkin` or `spring-cloud-sleuth-stream`. You can
|
||||
configure the exports using `spring.sleuth.sampler.percentage`. The passed
|
||||
value needs to be a double from `0.0` to `1.0` so it's not a percentage.
|
||||
For backwards compatibility reasons we're not changing the property name.
|
||||
NOTE: The `ProbabilityBasedSampler` is the default if you are using
|
||||
`spring-cloud-sleuth-zipkin`. You can
|
||||
configure the exports using `spring.sleuth.sampler.probability`. The passed
|
||||
value needs to be a double from `0.0` to `1.0`.
|
||||
|
||||
A sampler can be installed just by creating a bean definition, e.g:
|
||||
|
||||
@@ -54,6 +312,247 @@ TIP: You can set the HTTP header `X-B3-Flags` to `1` or when doing messaging you
|
||||
set `spanFlags` header to `1`. Then the current span will be forced to be exportable
|
||||
regardless of the sampling decision.
|
||||
|
||||
== Propagation
|
||||
|
||||
Propagation is needed to ensure activity originating from the same root
|
||||
are collected together in the same trace. The most common propagation
|
||||
approach is to copy a trace context from a client sending an RPC request
|
||||
to a server receiving it.
|
||||
|
||||
For example, when an downstream Http call is made, its trace context is
|
||||
sent along with it, encoded as request headers:
|
||||
|
||||
```
|
||||
Client Span Server Span
|
||||
┌──────────────────┐ ┌──────────────────┐
|
||||
│ │ │ │
|
||||
│ TraceContext │ Http Request Headers │ TraceContext │
|
||||
│ ┌──────────────┐ │ ┌───────────────────┐ │ ┌──────────────┐ │
|
||||
│ │ TraceId │ │ │ X─B3─TraceId │ │ │ TraceId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ ParentSpanId │ │ Extract │ X─B3─ParentSpanId │ Inject │ │ ParentSpanId │ │
|
||||
│ │ ├─┼─────────>│ ├────────┼>│ │ │
|
||||
│ │ SpanId │ │ │ X─B3─SpanId │ │ │ SpanId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ Sampled │ │ │ X─B3─Sampled │ │ │ Sampled │ │
|
||||
│ └──────────────┘ │ └───────────────────┘ │ └──────────────┘ │
|
||||
│ │ │ │
|
||||
└──────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
The names above are from https://github.com/openzipkin/b3-propagation[B3 Propagation],
|
||||
which is built-in to Brave and has implementations in many languages and
|
||||
frameworks.
|
||||
|
||||
Most users will use a framework interceptor which automates propagation.
|
||||
Here's how they might work internally.
|
||||
|
||||
Here's what client-side propagation might look like
|
||||
|
||||
```java
|
||||
// configure a function that injects a trace context into a request
|
||||
injector = tracing.propagation().injector(Request.Builder::addHeader);
|
||||
|
||||
// before a request is sent, add the current span's context to it
|
||||
injector.inject(span.context(), request);
|
||||
```
|
||||
|
||||
Here's what server-side propagation might look like
|
||||
|
||||
```java
|
||||
// configure a function that extracts the trace context from a request
|
||||
extracted = tracing.propagation().extractor(Request::getHeader);
|
||||
|
||||
// when a server receives a request, it joins or starts a new trace
|
||||
span = tracer.nextSpan(extracted, request);
|
||||
```
|
||||
|
||||
=== Propagating extra fields
|
||||
|
||||
Sometimes you need to propagate extra fields, such as a request ID or an alternate trace context.
|
||||
For example, if you are in a Cloud Foundry environment, you might want to pass the request ID:
|
||||
|
||||
```java
|
||||
// when you initialize the builder, define the extra field you want to propagate
|
||||
tracingBuilder.propagationFactory(
|
||||
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-vcap-request-id")
|
||||
);
|
||||
|
||||
// later, you can tag that request ID or use it in log correlation
|
||||
requestId = ExtraFieldPropagation.get("x-vcap-request-id");
|
||||
```
|
||||
|
||||
You may also need to propagate a trace context you aren't using. For example, you may be in an
|
||||
Amazon Web Services environment, but not reporting data to X-Ray. To ensure X-Ray can co-exist
|
||||
correctly, pass-through its tracing header like so.
|
||||
|
||||
```java
|
||||
tracingBuilder.propagationFactory(
|
||||
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, "x-amzn-trace-id")
|
||||
);
|
||||
```
|
||||
|
||||
==== Prefixed fields
|
||||
|
||||
You can also prefix fields, if they follow a common pattern. For example, the following will
|
||||
propagate the field "x-vcap-request-id" as-is, but send the fields "country-code" and "user-id"
|
||||
on the wire as "x-baggage-country-code" and "x-baggage-user-id" respectively.
|
||||
|
||||
Setup your tracing instance with allowed fields:
|
||||
|
||||
```java
|
||||
tracingBuilder.propagationFactory(
|
||||
ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY)
|
||||
.addField("x-vcap-request-id")
|
||||
.addPrefixedFields("baggage-", Arrays.asList("country-code", "user-id"))
|
||||
.build()
|
||||
);
|
||||
```
|
||||
|
||||
Later, you can call below to affect the country code of the current trace context
|
||||
|
||||
```java
|
||||
ExtraFieldPropagation.set("country-code", "FO");
|
||||
String countryCode = ExtraFieldPropagation.get("country-code");
|
||||
```
|
||||
|
||||
Or, if you have a reference to a trace context, use it explicitly
|
||||
|
||||
```java
|
||||
ExtraFieldPropagation.set(span.context(), "country-code", "FO");
|
||||
String countryCode = ExtraFieldPropagation.get(span.context(), "country-code");
|
||||
```
|
||||
|
||||
IMPORTANT: In comparison to previous versions of Sleuth, with
|
||||
Brave it's required to pass the list of baggage keys.
|
||||
There are two properties to achieve this. Via the `spring.sleuth.baggage-keys` you set keys
|
||||
that will get prefixed with `baggage-` for http calls and `baggage_` for messaging. You can also pass
|
||||
a list of prefixed keys that will be whitelisted without any prefix via
|
||||
`spring.sleuth.prefixed-keys` property.
|
||||
|
||||
==== Extracting a propagated context
|
||||
|
||||
The `TraceContext.Extractor<C>` reads trace identifiers and sampling status
|
||||
from an incoming request or message. The carrier is usually a request object
|
||||
or headers.
|
||||
|
||||
This utility is used in standard instrumentation like [HttpServerHandler](../instrumentation/http/src/main/java/sleuth/http/HttpServerHandler.java),
|
||||
but can also be used for custom RPC or messaging code.
|
||||
|
||||
`TraceContextOrSamplingFlags` is usually only used with `Tracer.nextSpan(extracted)`, unless you are
|
||||
sharing span IDs between a client and a server.
|
||||
|
||||
==== Sharing span IDs between client and server
|
||||
|
||||
A normal instrumentation pattern is creating a span representing the server
|
||||
side of an RPC. `Extractor.extract` might return a complete trace context when
|
||||
applied to an incoming client request. `Tracer.joinSpan` attempts to continue
|
||||
the this trace, using the same span ID if supported, or creating a child span
|
||||
if not. When span ID is shared, data reported includes a flag saying so.
|
||||
|
||||
Here's an example of B3 propagation:
|
||||
|
||||
```
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
Incoming Headers │ TraceContext │ │ TraceContext │
|
||||
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
|
||||
│ X─B3-TraceId │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ X─B3-ParentSpanId │─────────┼─┼> ParentSpanId │ │──────┼─┼> ParentSpanId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ X─B3-SpanId │─────────┼─┼> SpanId │ │──────┼─┼> SpanId │ │
|
||||
└───────────────────┘ │ │ │ │ │ │ │ │
|
||||
│ │ │ │ │ │ Shared: true │ │
|
||||
│ └───────────────┘ │ │ └───────────────┘ │
|
||||
└───────────────────┘ └───────────────────┘
|
||||
```
|
||||
|
||||
Some propagation systems only forward the parent span ID, detected when
|
||||
`Propagation.Factory.supportsJoin() == false`. In this case, a new span ID is
|
||||
always provisioned and the incoming context determines the parent ID.
|
||||
|
||||
Here's an example of AWS propagation:
|
||||
```
|
||||
┌───────────────────┐ ┌───────────────────┐
|
||||
x-amzn-trace-id │ TraceContext │ │ TraceContext │
|
||||
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
|
||||
│ Root │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ Parent │─────────┼─┼> SpanId │ │──────┼─┼> ParentSpanId │ │
|
||||
└───────────────────┘ │ └───────────────┘ │ │ │ │ │
|
||||
└───────────────────┘ │ │ SpanId: New │ │
|
||||
│ └───────────────┘ │
|
||||
└───────────────────┘
|
||||
```
|
||||
|
||||
Note: Some span reporters do not support sharing span IDs. For example, if you
|
||||
set `Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive)`, disable join
|
||||
via `Tracing.Builder.supportsJoin(false)`. This will force a new child span on
|
||||
`Tracer.joinSpan()`.
|
||||
|
||||
==== Implementing Propagation
|
||||
|
||||
`TraceContext.Extractor<C>` is implemented by a `Propagation.Factory` plugin. Internally, this code
|
||||
will create the union type `TraceContextOrSamplingFlags` with one of the following:
|
||||
* `TraceContext` if trace and span IDs were present.
|
||||
* `TraceIdContext` if a trace ID was present, but not span IDs.
|
||||
* `SamplingFlags` if no identifiers were present
|
||||
|
||||
Some `Propagation` implementations carry extra data from point of extraction (ex reading incoming
|
||||
headers) to injection (ex writing outgoing headers). For example, it might carry a request ID. When
|
||||
implementations have extra data, here's how they handle it.
|
||||
* If a `TraceContext` was extracted, add the extra data as `TraceContext.extra()`
|
||||
* Otherwise, add it as `TraceContextOrSamplingFlags.extra()`, which `Tracer.nextSpan` handles.
|
||||
|
||||
== Current Tracing Component
|
||||
|
||||
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.
|
||||
|
||||
== Current Span
|
||||
|
||||
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.
|
||||
|
||||
=== Setting a span in scope manually
|
||||
|
||||
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.
|
||||
|
||||
```java
|
||||
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`.
|
||||
|
||||
```java
|
||||
try (SpanInScope cleared = tracer.withSpanInScope(null)) {
|
||||
startBackgroundThread();
|
||||
}
|
||||
```
|
||||
|
||||
== Instrumentation
|
||||
|
||||
Spring Cloud Sleuth instruments all your Spring application
|
||||
@@ -74,29 +573,24 @@ NOTE: Remember that tags are only collected and exported if there is a
|
||||
danger of accidentally collecting too much data without configuring
|
||||
something).
|
||||
|
||||
NOTE: Currently the instrumentation in Spring Cloud Sleuth is eager - it means that
|
||||
we're actively trying to pass the tracing context between threads. Also timing events
|
||||
are captured even when sleuth isn't exporting data to a tracing system.
|
||||
This approach may change in the future towards being lazy on this matter.
|
||||
|
||||
== Span lifecycle
|
||||
|
||||
You can do the following operations on the Span by means of *org.springframework.cloud.sleuth.Tracer* interface:
|
||||
You can do the following operations on the Span by means of *brave.Tracer*:
|
||||
|
||||
- <<creating-and-closing-spans, start>> - when you start a span its name is assigned and start timestamp is recorded.
|
||||
- <<creating-and-closing-spans, close>> - the span gets finished (the end time of the span is recorded) and if
|
||||
the span is *exportable* then it will be eligible for collection to Zipkin.
|
||||
The span is also removed from the current thread.
|
||||
- <<creating-and-finishing-spans, start>> - when you start a span its name is assigned and start timestamp is recorded.
|
||||
- <<creating-and-finishing-spans, close>> - the span gets finished (the end time of the span is recorded) and if
|
||||
the span is *sampled* then it will be eligible for collection to e.g. Zipkin.
|
||||
- <<continuing-spans, continue>> - a new instance of span will be created whereas it will be a copy of the
|
||||
one that it continues.
|
||||
- <<continuing-spans, detach>> - the span doesn't get stopped or closed. It only gets removed from the current thread.
|
||||
- <<creating-spans-with-explicit-parent, create with explicit parent>> - you can create a new span and set an explicit parent to it
|
||||
|
||||
TIP: Spring creates the instance of `Tracer` for you. In order to use it all you need is to just autowire it.
|
||||
TIP: Spring Cloud Sleuth creates the instance of `Tracer` for you. In order to use it,
|
||||
all you need is to just autowire it.
|
||||
|
||||
=== Creating and closing spans [[creating-and-closing-spans]]
|
||||
=== Creating and finishing spans [[creating-and-finishing-spans]]
|
||||
|
||||
You can manually create spans by using the *Tracer* interface.
|
||||
You can manually create spans by using the *Tracer*.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -106,7 +600,7 @@ include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/
|
||||
In this example we could see how to create a new instance of span. Assuming that there already
|
||||
was a span present in this thread then it would become the parent of that span.
|
||||
|
||||
IMPORTANT: Always clean after you create a span! Don't forget to close a span if you want to send it to Zipkin.
|
||||
IMPORTANT: Always clean after you create a span! Don't forget to finish a span if you want to send it to Zipkin.
|
||||
|
||||
IMPORTANT: If your span contains a name greater than 50 chars, then that name will
|
||||
be truncated to 50 chars. Your names have to be explicit and concrete. Big names lead to
|
||||
@@ -121,39 +615,28 @@ situation might be (of course it all depends on the use-case):
|
||||
- *Hystrix* - executing a Hystrix command is most likely a logical part of the current processing. It's in fact
|
||||
only a technical implementation detail that you wouldn't necessarily want to reflect in tracing as a separate being.
|
||||
|
||||
The continued instance of span is equal to the one that it continues:
|
||||
[source,java]
|
||||
----
|
||||
Span continuedSpan = this.tracer.continueSpan(spanToContinue);
|
||||
assertThat(continuedSpan).isEqualTo(spanToContinue);
|
||||
----
|
||||
|
||||
To continue a span you can use the *Tracer* interface.
|
||||
To continue a span you can use *brave.Tracer*.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_continuation,indent=0]
|
||||
----
|
||||
|
||||
IMPORTANT: Always clean after you create a span! Don't forget to detach a span if some work was done started in one
|
||||
thread (e.g. thread X) and it's waiting for other threads (e.g. Y, Z) to finish.
|
||||
Then the spans in the threads Y, Z should be detached at the end of their work. When the results are collected
|
||||
the span in thread X should be closed.
|
||||
|
||||
=== Creating spans with an explicit parent [[creating-spans-with-explicit-parent]]
|
||||
|
||||
There is a possibility that you want to start a new span and provide an explicit parent of that span.
|
||||
Let's assume that the parent of a span is in one thread and you want to start a new span in another thread. The
|
||||
`startSpan` method of the `Tracer` interface is the method you are looking for.
|
||||
Let's assume that the parent of a span is in one thread and you want to start a new span in another thread.
|
||||
In Brave, whenever you call `nextSpan()`, it's creating one in reference
|
||||
to the span being currently in scope. It's enough to just put
|
||||
the span in scope and then call `nextSpan()`, as presented in the example below:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java[tags=manual_span_joining,indent=0]
|
||||
----
|
||||
|
||||
IMPORTANT: After having created such a span remember to close it. Otherwise you will see a lot of warnings in your logs
|
||||
related to the fact that you have a span present in the current thread other than the one you're trying to close.
|
||||
What's worse your spans won't get closed properly thus will not get collected to Zipkin.
|
||||
IMPORTANT: After having created such a span remember to finish it, otherwise it will not get
|
||||
reported to e.g. Zipkin
|
||||
|
||||
== Naming spans
|
||||
|
||||
@@ -344,82 +827,14 @@ if executed with a value of `15` will lead to setting of a tag with a String val
|
||||
|
||||
== Customizations
|
||||
|
||||
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:
|
||||
|
||||
* via Spring Integration
|
||||
* via HTTP
|
||||
|
||||
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.
|
||||
// TODO: Update this
|
||||
|
||||
=== Spring Integration
|
||||
|
||||
For Spring Integration there are 2 interfaces responsible for creation of a Span from a `Message`.
|
||||
These are:
|
||||
|
||||
- `MessagingSpanTextMapExtractor`
|
||||
- `MessagingSpanTextMapInjector`
|
||||
|
||||
You can override them by providing your own implementation.
|
||||
|
||||
=== HTTP
|
||||
|
||||
For HTTP there are 2 interfaces responsible for creation of a Span from a `Message`.
|
||||
These are:
|
||||
|
||||
- `HttpSpanExtractor`
|
||||
- `HttpSpanInjector`
|
||||
|
||||
You can override them by providing your own implementation.
|
||||
|
||||
=== Example
|
||||
|
||||
Let's assume that instead of the standard Zipkin compatible tracing HTTP header names
|
||||
you have
|
||||
|
||||
* for trace id - `correlationId`
|
||||
* for span id - `mySpanId`
|
||||
|
||||
This is a an example of a `SpanExtractor`
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=extractor,indent=0]
|
||||
----
|
||||
|
||||
And you could register it like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
|
||||
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:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java[tags=injector,indent=0]
|
||||
----
|
||||
|
||||
And you could register them like this:
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
// TODO: Update this
|
||||
|
||||
=== TraceFilter
|
||||
|
||||
@@ -436,19 +851,6 @@ add to the Span a tag with key `custom` and a value `tag`.
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java[tags=response_headers,indent=0]
|
||||
----
|
||||
|
||||
=== Custom SA tag in Zipkin
|
||||
|
||||
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.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin2/ZipkinSpanReporterTests.java[tags=service_name,indent=0]
|
||||
----
|
||||
|
||||
IMPORTANT: Remember not to add both `peer.service` tag and the `SA` tag! You have to add only `peer.service`.
|
||||
|
||||
=== Custom service name
|
||||
|
||||
By default Sleuth assumes that when you send a span to Zipkin, you want the span's service name
|
||||
@@ -467,26 +869,23 @@ spring.zipkin.service.name: foo
|
||||
Before 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:
|
||||
|
||||
[source,yaml]
|
||||
Example. If you register two beans of `SpanAdjuster` type:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
SpanAdjuster customSpanAdjuster() {
|
||||
return span -> span.toBuilder().name(scrub(span.getName())).build();
|
||||
}
|
||||
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 just before it gets sent to Zipkin.
|
||||
|
||||
IMPORTANT: Your `SpanReporter` should inject the `SpanAdjuster` and
|
||||
allow span manipulation before the actual reporting is done.
|
||||
This will lead in changing the name of the reported span to `foo bar`, just before it gets reported (e.g. to Zipkin).
|
||||
|
||||
=== Host locator
|
||||
|
||||
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.
|
||||
@@ -520,68 +919,15 @@ Zipkin's service id inside the URL (example for `zipkinserver` service id)
|
||||
spring.zipkin.baseUrl: http://zipkinserver/
|
||||
----
|
||||
|
||||
== Span Data as Messages
|
||||
== Zipkin Stream Span Consumer
|
||||
|
||||
IMPORTANT: The suggested approach is to use the Zipkin's
|
||||
native support for message based span sending. Starting from
|
||||
Edgware Zipkin Stream server is deprecated and in Finchley
|
||||
it got removed.
|
||||
|
||||
You can accumulate and send span data over
|
||||
http://cloud.spring.io/spring-cloud-stream[Spring Cloud Stream] by
|
||||
including the `spring-cloud-sleuth-stream` jar as a dependency, and
|
||||
adding a Channel Binder implementation
|
||||
(e.g. `spring-cloud-starter-stream-rabbit` for RabbitMQ or
|
||||
`spring-cloud-starter-stream-kafka` for Kafka). This will
|
||||
automatically turn your app into a producer of messages with payload
|
||||
type `Spans`. The channel name to which the spans will be sent
|
||||
is called `sleuth`.
|
||||
|
||||
=== Zipkin Consumer
|
||||
|
||||
Please refer to the http://cloud.spring.io/spring-cloud-static/Dalston.SR4/multi/multi__span_data_as_messages.html#_zipkin_consumer[Dalston Documentaion]
|
||||
on how to create a Stream Zipkin server. That approach has been
|
||||
deprecated in Edgware and removed in Finchley release.
|
||||
|
||||
=== Custom Consumer
|
||||
|
||||
A custom consumer can also easily be implemented using
|
||||
`spring-cloud-sleuth-stream` and binding to the `SleuthSink`. Example:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@EnableBinding(SleuthSink.class)
|
||||
@SpringBootApplication(exclude = SleuthStreamAutoConfiguration.class)
|
||||
@MessageEndpoint
|
||||
public class Consumer {
|
||||
|
||||
@ServiceActivator(inputChannel = SleuthSink.INPUT)
|
||||
public void sink(Spans input) throws Exception {
|
||||
// ... process spans
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: the sample consumer application above explicitly excludes
|
||||
`SleuthStreamAutoConfiguration` so it doesn't send messages to itself,
|
||||
but this is optional (you might actually want to trace requests into
|
||||
the consumer app).
|
||||
|
||||
In order to customize the polling mechanism you can create a bean of `PollerMetadata` type
|
||||
with name equal to `StreamSpanReporter.POLLER`. Here you can find an example of such a configuration.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-stream/src/test/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfigurationTest.java[tags=custom_poller,indent=0]
|
||||
----
|
||||
|
||||
== Metrics
|
||||
|
||||
Currently Spring Cloud Sleuth registers very simple metrics related to spans.
|
||||
It's using the http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html#production-ready-recording-metrics[Spring Boot's metrics support]
|
||||
to calculate the number of accepted and dropped spans. Each time a span gets
|
||||
sent to Zipkin the number of accepted spans will increase. If there's an error then
|
||||
the number of dropped spans will get increased.
|
||||
on how to create a Stream Zipkin server.
|
||||
|
||||
== Integrations
|
||||
|
||||
@@ -672,7 +1018,6 @@ Via the `TraceWebFilter` all sampled incoming requests result in creation of a S
|
||||
like to skip via the `spring.sleuth.web.skipPattern` property. If you have `ManagementServerProperties` on classpath then
|
||||
its value of `contextPath` gets appended to the provided skip pattern.
|
||||
|
||||
|
||||
=== HTTP client integration
|
||||
|
||||
==== Synchronous Rest Template
|
||||
@@ -686,19 +1031,9 @@ If you create a `RestTemplate` instance with a `new` keyword then the instrument
|
||||
|
||||
==== Asynchronous Rest Template
|
||||
|
||||
IMPORTANT: A traced version of an `AsyncRestTemplate` bean is registered for you out of the box. If you
|
||||
have your own bean you have to wrap it in a `TraceAsyncRestTemplate` representation. The best solution
|
||||
is to only customize the `ClientHttpRequestFactory` and / or `AsyncClientHttpRequestFactory`.
|
||||
*If you have your own `AsyncRestTemplate` and you don't wrap it your calls WILL NOT GET TRACED*.
|
||||
|
||||
Custom instrumentation is set to create and close Spans upon sending and receiving requests. You can customize the `ClientHttpRequestFactory`
|
||||
and the `AsyncClientHttpRequestFactory` by registering your beans. Remember to use tracing compatible implementations (e.g. don't forget to
|
||||
wrap `ThreadPoolTaskScheduler` in a `TraceAsyncListenableTaskExecutor`). Example of custom request factories:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfigurationTests.java[tags=async_template_factories,indent=0]
|
||||
----
|
||||
IMPORTANT: Starting with Sleuth `2.0.0` we no longer register
|
||||
a bean of `AsyncRestTemplate` type. It's up to you to create such
|
||||
a bean. Then we will instrument it.
|
||||
|
||||
To block the `AsyncRestTemplate` features set `spring.sleuth.web.async.client.enabled` to `false`.
|
||||
To disable creation of the default `TraceAsyncClientHttpRequestFactoryWrapper` set `spring.sleuth.web.async.client.factory.enabled`
|
||||
@@ -711,7 +1046,7 @@ can see an example of how to set up such a custom `AsyncRestTemplate`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/MultipleAsyncRestTemplateTests.java[tags=custom_async_rest_template,indent=0]
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_async_rest_template,indent=0]
|
||||
----
|
||||
|
||||
==== WebClient
|
||||
@@ -800,7 +1135,7 @@ can see an example of how to set up such a custom `Executor`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/MultipleAsyncRestTemplateTests.java[tags=custom_executor,indent=0]
|
||||
include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/MultipleAsyncRestTemplateTests.java[tags=custom_executor,indent=0]
|
||||
----
|
||||
|
||||
=== Messaging
|
||||
|
||||
Reference in New Issue
Block a user