Brave 4.10
Brave v4.10 adds Netty and Apache HC caching instrumentation, improves MDC integration and adds tracing tools.
Netty Http Server instrumentation
Netty is a popular I/O library used underneath popular frameworks such as gRPC,
CXF and Ratpack. If you are using Brave with a Netty library now, you likely
configure a framework-specific hook. If you aren't writing a Netty framework or
configuring an http pipeline, you can skip this section.
Due to popular demand, we've added trace instrumentation for any
netty-codec-http
pipeline. Add NettyHttpTracing.serverHandler()
between
infrastructure and application handlers to precisely measure the server-side of
http calls. This is a more precise alternative to doing so at higher layers,
such as servlet, because it is closer to the wire. Thanks to @songxin1990 for
the hard work on this, as well @normanmaurer and @nicmunroe for review.
Depend on io.zipkin.brave:brave-instrumentation-netty-codec-http
and
configure your handlers like so to transparently trace http server calls:
NettyHttpTracing nettyHttpTracing = NettyHttpTracing.create(httpTracing);
ChannelPipeline pipeline = ch.pipeline();
... add your infrastructure handlers, in particular HttpRequestDecoder and HttpResponseEncoder
pipeline.addLast("tracing", nettyHttpTracing.serverHandler());
... add your application handlers
Adding parentId
to logging contexts
Brave 4.x not only provides tracing, but also logging integration. Even when
a request isn't sampled for tracing, you can see stable trace identifiers in
logs for correlation purposes or for integration with other tools.
Before, we attached traceId
and spanId
to logging contexts such as SLF4J,
log4j or log4j2. Some integrations, such as PCF metrics, parse logs to create
traces independent of Zipkin. We added parentId
to the trace context to
ensure tooling like this can place spans at the right place in the trace tree.
Apache httpclient-cache integration
While commonly done, Apache 4.x HttpClient interceptors cannot reliably attach
trace identifiers to logging contexts. Instead, Brave decorates
HttpClientBuilder
to allow both tracing and logging to work properly. One
downside to decorating builders is there are more than one.
You can now substitute TracingCachingHttpClientBuilder
to trace and integrate
logs with caching http clients.
New tools for instrumentors
Brave serves at least two audiences: those who want to configure tracing and
those who provide trace instrumentation to others. This section is for those
writing tracing code on behalf of others.
Better Tracer.toString()
Troubleshooting state can be difficult due to overlapping scopes. We've added
a couple things to Tracer.toString()
which can significantly demystify state
when debugging. Note: this format is subject to change based on your feedback!
Tracer.toString()
always includes the reporter (where spans are going).
Tracer{reporter=AsyncReporter(OkHttpSender(http://myhost:9411/api/v2/spans))}
When spans are in-flight, Tracer.toString()
includes a snapshot of each even
if the thread calling toString()
is not actively tracing. This part might
change based on feedback. For example, it could be moved to a utility which you
can plumb to an http endpoint (as there could be very many spans in flight!).
Tracer{inFlight=[{"traceId":"48485a3953bb61246b221d5bc9e6496c","id":"6b221d5bc9e6496c","timestamp":1461750491274000,"localEndpoint":{"serviceName":"my-service"}}], reporter=...
When the current thread is tracing, Tracer.toString()
includes the current
trace context. Most tracing bugs are about this part.. for example, a callback
wasn't scoped properly. Knowing what's in scope helps those writing tracing
code fix bugs faster.
Tracer{currentSpan=48485a3953bb61246b221d5bc9e6496c/6b221d5bc9e6496c, reporter=...
ThreadLocalSpan
Sometimes you have to instrument a library where There's no attribute namespace
shared across request and response. For this scenario, you can use
ThreadLocalSpan
to temporarily store the span between callbacks.
Here's an example:
class MyFilter extends Filter {
final ThreadLocalSpan threadLocalSpan;
public void onStart(Request request) {
// Assume you have code to start the span and add relevant tags...
// We now set the span in scope so that any code between here and
// the end of the request can see it with Tracer.currentSpan()
threadLocalSpan.set(span);
}
public void onFinish(Response response, Attributes attributes) {
// as long as we are on the same thread, we can read the span started above
Span span = threadLocalSpan.remove();
if (span == null) return;
// Assume you have code to complete the span
}
}