Wingtips is a distributed tracing solution for Java based on the Google Dapper paper.
This module is a plugin extension module of the core Wingtips library and contains support for distributed tracing in a
Spring WebFlux
environment. In particular it provides a Spring WebFilter
for handling overall-request spans that your server
creates in response to incoming requests, and a Spring ExchangeFilterFunction
to handle HTTP client child-span and
tracing propagation for Spring's new reactive
WebClient.
The WebFilter
is analogous to a Servlet filter from traditional Servlet-based frameworks, and the
ExchangeFilterFunction
is analogous to an interceptor for a traditional blocking HTTP client.
NOTE: As mentioned, this module is for Spring WebFlux specifically, for both serverside and clientside WebFlux
support. If you're looking for Spring Web MVC support, you should use RequestTracingFilter
from
wingtips-servlet-api for Servlet-based Spring Web MVC serverside, and the various features
of wingtips-spring for the older Spring HTTP clients.
NOTES:
- The Wingtips Spring Boot 2 WebFlux sample project shows these features in action.
- More details can be found in the javadocs for the various classes found in this
wingtips-spring-webflux
module.
Register a WingtipsSpringWebfluxWebFilter
for automatically handling tracing duties for incoming requests
In one of your Spring config classes:
@Bean
public WingtipsSpringWebfluxWebFilter wingtipsSpringWebfluxWebFilter() {
return WingtipsSpringWebfluxWebFilter
.newBuilder()
.withUserIdHeaderKeys(myAppUserIdHeaderKeys) // <- optional
.build();
}
This will register WingtipsSpringWebfluxWebFilter
as a Spring WebFilter
so it will handle all requests.
Later, in a controller endpoint:
import static com.nike.wingtips.spring.webflux.WingtipsSpringWebfluxUtils.tracingStateFromContext;
import static com.nike.wingtips.spring.webflux.WingtipsSpringWebfluxUtils.tracingStateFromExchange;
// ...
@GetMapping(SOME_ENDPOINT_PATH)
@ResponseBody
Mono<String> someEndpoint(ServerWebExchange exchange) {
// The tracing state is embedded in the ServerWebExchange.
TracingState tracingStateFromExchange = tracingStateFromExchange(exchange);
return Mono
.subscriberContext()
.map(context -> {
// The tracing state is also embedded and available in the Mono Context.
TracingState tracingStateFromContext = tracingStateFromContext(context);
assert (tracingStateFromContext == tracingStateFromExchange);
return SOME_ENDPOINT_PAYLOAD;
});
}
As shown above, the correct TracingState
for the request will be embedded in the ServerWebExchange
attributes
for the request, and it will be embedded in the Project Reactor (Mono/Flux) Context
. You can use this TracingState
with any of the Java 8 helpers from wingtips-java8 to propagate the TracingState
when hopping
threads. See the wingtips-java8 readme's usage examples for details.
(NOTE: The same TracingState
may be on the current thread when the controller endpoint executes, or it may not be.
It depends on the thread that Spring ends up using to subscribe to the Mono
/Flux
. This can be affected by
something as simple as a @RequestBody
annotation. Therefore it's recommended that you not rely on having the
TracingState
attached to the thread when your endpoint method executes. Instead, you should extract it from the
ServerWebExchange
or the Mono/Flux Context
as shown above, as it will always be available there.)
Register a WingtipsSpringWebfluxExchangeFilterFunction
for automatic tracing propagation when using Spring's reactive WebClient
// Create a WebClient with the Wingtips filter for automatic subspan and tracing propagation.
WebClient webClientWithWingtips = WebClient
.builder()
.filter(WingtipsSpringWebfluxExchangeFilterFunction.DEFAULT_IMPL) // <- or use a constructor with config options.
.build();
// ... later, when ready to execute your request:
Mono<ClientResponse> responseMono = webClientWithWingtips
.get()
.uri(someRequestUri)
.attribute(TracingState.class.getName(), overallRequestTracingState)
.exchange();
Note that in order for the WingtipsSpringWebfluxExchangeFilterFunction
to know what the correct TracingState
is
that it should use, the correct TracingState
must be supplied in the request
attributes when the call is executed (i.e. via the WebClient
's exchange()
or retrieve()
methods).
To supply the TracingState
in the request attributes, just provide an attribute with the key of
TracingState.class.getName()
and the value of the TracingState
you want the filter to use (as shown in the example
above).
As mentioned in the previous WingtipsSpringWebfluxWebFilter
usage section, you can
retrieve the overall request TracingState
(for a server using WingtipsSpringWebfluxWebFilter
) in two reliable ways:
- Extracted from the
ServerWebExchange
using theWingtipsSpringWebfluxUtils.tracingStateFromExchange(...)
helper method. - Extracted from the Project Reactor Mono/Flux
Context
using theWingtipsSpringWebfluxUtils.tracingStateFromContext(...)
helper method.
This module contains the following main features/classes:
WingtipsSpringWebfluxWebFilter
- A serverside SpringWebFilter
that handles all of the work for enabling a new span when a request comes in and completing it when the request finishes. This filter:- Automatically extracts parent span information from the incoming request headers for the new span if available.
- Sets the
X-B3-TraceId
response header to the Trace ID for each request. - Pulls the user ID from incoming request headers and populates it on the new span for you, assuming you've configured it to look for user ID header(s) (this can be ignored if you don't have user ID headers).
- By default, this filter will tag and name spans based on metadata from the request and response using the ZipkinHttpTagStrategy and SpringWebfluxServerRequestTagAdapter. You can choose different implementations using the builder if necessary.
- All configuration options are controlled via the
WingtipsSpringWebfluxWebFilter.Builder
.
WingtipsSpringWebfluxExchangeFilterFunction
- A clientsideExchangeFilterFunction
interceptor for Spring's reactiveWebClient
HTTP client that automatically propagates Wingtips tracing information on the downstream call's request headers, with an option to surround the downstream call in a subspan. This interceptor uses a small extension ofZipkinHttpTagStrategy
by default to name and tag any created subspans. See the Default HTTP Tags section in the main readme for details on what default tags you get, and the javadocs for SpringWebfluxClientRequestZipkinTagStrategy for details on the SpringWebClient
extras. You can use a different tag and naming strategy (and/or tag adapter) if desired by passing it in when constructing the interceptor.WingtipsSpringWebfluxUtils
- A class with static helper methods to facilitate some of the features provided by this module. In particular for end-user usage:tracingStateFromExchange(...)
- pulls Wingtips tracing state from aServerWebExchange
, populated byWingtipsSpringWebfluxWebFilter
.tracingStateFromContext(...)
- pulls Wingtips tracing state from a Project Reactorreactor.util.context.Context
. This is populated byWingtipsSpringWebfluxWebFilter
for serverside flows, andWingtipsSpringWebfluxExchangeFilterFunction
for clientside flows.
For general Wingtips information please see the base project README.md.
This module does not export any transitive Spring dependencies to prevent version conflicts with whatever Spring environment you're running in.
This should not affect most users since this library is likely to be used in a Spring environment where the
spring-webflux
dependency is already on the classpath at runtime, however if you receive class-not-found errors
related to Spring WebFlux classes found in spring-webflux
then you'll need to pull the
org.springframework:spring-webflux
dependency into your project. Library authors who wish to build on functionality
in this module might need to do this.
This module was built using version 5.1.9.RELEASE
of spring-webflux
, but many other versions of Spring should work
fine, both older and newer.