Spring Cloud Sleuth currently is an autoconfiguration over Brave. It also consists of various instrumentation mechanisms for libraries that are not supported by Brave (e.g. Spring Cloud Circuitbreaker).
We would like to abstract Brave so that Spring Cloud Sleuth becomes an autoconfiguration for any tracer implementation that is compatible with Spring Cloud Sleuth. That way Spring Cloud Sleuth in its core module would consist of an API and various tracer implementations would implement that API which would also allow automatic instrumentation of libraries that are supported by Spring Cloud Sleuth.
## OpenTelemetry Support
Thanks to doing this abstraction we are able to support new tracer implementations, not only Brave. We've decided to add support for the OpenTelemetry SDK as the second one. If in the future if we decide to add new tracers then it will be just a matter of adding a new module that bridges to the Spring Cloud Sleuth one. Thanks to abstraction of tests as well we will be easily able to plug that tracer mechanism into our current suite of tests.
- adds Spring Cloud Function instrumentation
- adds Operators to manually provide instrumentation for Fluxes
- introduces Manual instrumentation mode for Reactor
TODO: Documentation (will add it soon)
related gh-1684
Brave's SpanHandler can report natively in other formats which have different
constraints than Zipkin and often extensions to the data model.
This change ports all tests away from Zipkin's types so that it is more clear
what's actually recorded vs what's a side-effect of Zipkin conversion.
This removes `BlockingQueueSpanReporter` which was never released, also.
`SpanHandler` is the base type for the now deprecated `FinishedSpanHandler`.
Notable, it can not just handle things at the end of a recording, but also the
beginning.
For example, this permits set-once baggage without the HTTP abstraction:
```java
static final BaggageField EPOCH_SECONDS = BaggageField.create("epoch_seconds");
static final class RootOnlyBaggage extends SpanHandler {
@Override
public boolean begin(TraceContext context, MutableSpan span, @Nullable TraceContext parent) {
if (EPOCH_SECONDS.getValue(context) == null) { // only set at the first span
long epochSeconds = System.currentTimeMillis() / 1000;
EPOCH_SECONDS.updateValue(context, String.valueOf(epochSeconds));
}
return true;
}
@Override public boolean end(TraceContext context, MutableSpan span, Cause cause) {
Tags.BAGGAGE_FIELD.tag(EPOCH_SECONDS, context, span);
return true;
}
}
```
As the parent is available, it can also facilitate advanced tasks like counting
children, or summarizing entire local roots.
See https://github.com/openzipkin/brave/tree/master/brave/src/test/java/brave/features/handler
and https://github.com/openzipkin/brave/blob/master/brave/src/main/java/brave/handler/SpanHandler.java for more
To reduce confusion and overhead, the following custom spring-messaging headers added in Sleuth 1.0 are no longer sent, and a log warning is issued once if they are by outside code.
* spanId
* spanSampled
* spanParentSpanId
* spanTraceId
* spanFlags
Sending the above headers actually increases the headers by up to 10 because they are duplicated in the "native" part of messages. This overhead is extreme especially if messages never leave the process.
The solution is to only send [b3 single format](https://github.com/openzipkin/b3-propagation#single-header), which has been in sleuth since 2.0 and is compatible with JMS. The B3 single format is always parsed and takes precedence, even if multiple headers are sent, so this is a safe change.
Note: Unlike RPC, messaging spans never join with their parent. Better performance is achieved by not propagating the producer's parentId downstream.
Note: Deprecated spring-messaging headers such "spanTraceId" as are still read in Sleuth 3.x. However, they will not be at some point in the future. Please pay attention to the log messages and update your code if you are accidentally using them.
* Updates to latest Sampling infrastructure
Brave recently switched to an interface model for higher level sampling
like HTTP. So, `HttpSampler` -> `SamplingFunction<HttpRequest>`. Don't
worry because `HttpSampler` was retrofitted as a `SamplingFunction`.
This change moves to the higher interface, avoiding deprecated methods
and such. More interestingly, this weaves in support for `RpcTracing`,
which *finally* introduces RPC sampling the same way. Specifically, this
adds `SamplingFunction<RpcRequest>` under the same conventions as HTTP.
Most immediately, this can be used here in gRPC and Dubbo, as
autoconfiguration exists. It also works with any autoconfiguration that
isn't here, such as Armeria.
Ex. Here's a sampler that traces 100 "GetUserToken" requests per second. This
doesn't start new traces for requests to the health check service. Other
requests will use a global rate provided by the tracing component.
```java
import static brave.rpc.RpcRequestMatchers.methodEquals;
import static brave.rpc.RpcRequestMatchers.serviceEquals;
import static brave.sampler.Matchers.and;
--snip--
@Bean(name = ServerSampler.NAME)
SamplerFunction<RpcRequest> myRpcSampler() {
Matcher<RpcRequest> userAuth = and(
serviceEquals("users.UserService"),
methodEquals("GetUserToken")
);
return RpcRuleSampler.newBuilder()
.putRule(serviceEquals("grpc.health.v1.Health"), Sampler.NEVER_SAMPLE)
.putRule(userAuth, RateLimitingSampler.create(100)).build();
}
```