Zipkin 2
In version 1.31, we introduced our v2 http api, availing dramatically simplified data types. Zipkin 2 is the effort to move all infrastructure towards that model, while still remaining backwards compatible.
What's new?
The core java library (under the package zipkin2) has model, codec and storage types. This includes a bounded in-memory storage component used in test environments.
The following artifacts are new and can coexist with previous ones.
io.zipkin.zipkin2:zipkin:2.0.0
< core libraryio.zipkin.zipkin2:zipkin-storage-elasticsearch:2.0.0
< first v2 native storage driver
Note: If you are using io.zipkin.java:zipkin
and io.zipkin.zipkin2:zipkin
, use version 2.0.0 (or later) for both as we still maintain the old libraries.
What's next?
There are a few storage implementations in-flight and some may port to the new libraries. Next, we will add a v2 native transport library and work on a Spring Boot 2 based server. Expect incremental progress along the way. Please join us on gitter if you have ideas!
The server itself is still the same
Note: if you are only using or configuring Zipkin, there's little impact. Zipkin server hasn't changed, you just upgrade it. If you have java tracing setup, read the below. Otherwise, you are done unless you want extra details.
Changing java applications to use Zipkin v2 format
Java applications often use the zipkin-reporter project directly or indirectly to send data to Zipkin collectors. Our version 2 json format is smaller and measurably more efficient.
Once you've upgraded your Zipkin servers, opt-into the version 2 format like this:
Ex:
/** Configuration for how to send spans to Zipkin */
@Bean Sender sender() {
- return OkHttpSender.create("http://your_host:9411/api/v1/spans");
+ return OkHttpSender.json("http://your_host:9411/api/v2/spans");
}
/** Configuration for how to buffer spans into messages for Zipkin */
- @Bean Reporter<Span> reporter() {
- return AsyncReporter.builder(sender()).build();
+ @Bean Reporter<Span> spanReporter() {
+ return AsyncReporter.v2(sender()).build();
}
If you are using Brave directly, you can stick the v2 reporter here:
return Tracing.newBuilder()
- .reporter(reporter()).build();
+ .spanReporter(spanReporter())
If you are using Spring XML, the related change looks like this:
- <bean id="sender" class="zipkin.reporter.okhttp3.OkHttpSender" factory-method="create"
+ <bean id="sender" class="zipkin.reporter.okhttp3.OkHttpSender" factory-method="json"
destroy-method="close">
- <constructor-arg type="String" value="http://localhost:9411/api/v1/spans"/>
+ <constructor-arg type="String" value="http://localhost:9411/api/v2/spans"/>
</bean>
<bean id="tracing" class="brave.spring.beans.TracingFactoryBean">
<property name="reporter">
<bean class="brave.spring.beans.AsyncReporterFactoryBean">
+ <property name="encoder" value="JSON_V2"/>
What's new in the Zipkin v2 library
Zipkin v2 libraries are under the zipkin2
java package and the io.zipkin.zipkin2
maven group ID. The core library has a few changes, which mostly cleanup or pare down features we had before. Here are some highlights:
Span now uses validated strings as opposed to parsed objects
Our new json encoder is 2x as fast as prior due to factors including a validation approach. For example, before we used the java long type to represent a 64-bit ID and a 32-bit integer to represent an ipv4 address. Most of the time, IDs are and IPs are transmitted and stored as strings. This resulted in needless expensive conversions. By switching to this, using other serialization libraries is easier, too, as you don't need custom type converters.
Ex.
- Endpoint.builder().serviceName("tweetie").ipv4(192 << 24 | 168 << 16 | 1).build());
+ Endpoint.newBuilder().serviceName("tweetie").ip("192.168.0.1").build());
protip: if you have an old endpoint, you can do endpoint.toV2()
on it!
Span now uses auto-value instead of public final fields
We originally had public final fields for our model types (borrowing from square wire style). This has a slight glitch which is that data transformations can't use method references (as fields aren't methods!). This is cleaned up now.
- assertThat(spans).extracting(s -> s.duration)
+ assertThat(spans).extracting(Span::duration)
Asynchronous operations are now cancelable
Most will not make custom Zipkin servers, but those making storage or transport plugins have a cleaner api.
Borrowing heavily from Square Retrofit and OkHttp, Zipkin storage interfaces return a Call object, which represents a single unit of work, such as storing spans. This provides means to either synchronously invoke the command, pass a callback, or compose with your favorite library. Unlike before, calls are cancelable.
For example, before, if you wanted to write integration tests that synchronously invoke storage, you'd need to play callback games. These are gone.
- CallbackCaptor<Void> callback = new CallbackCaptor<>();
- storage().asyncSpanConsumer().accept(spans, callback);
- callback.get();
+ storage.spanConsumer().accept(spans).execute();
As an implementor, the whole thing is simpler especially combined with validated string IDs
- @Override public void getTrace(long traceIdHigh, long traceIdLow, Callback<List<Span>>) {
- String traceIdHex = Util.toLowerHex(traceIdHigh, traceIdLow);
+ @Override public Call<List<Span>> getTrace(String traceId) {
(json) Codec libraries are cleaned up
We've introduced SpanBytesEncoder
and SpanBytesDecoder
instead of the catch-all Codec
type from v1. When writing zipkin-reporter, we noticed that almost all applications do not need decode logic (as they simply serialize and send out of process). For those writing data to Zipkin, we can serialize either the old format or the new with SpanBytesEncoder.JSON_V1
or SpanBytesEncoder.JSON_V2
accordingly. It is important to note that writing v1 format does not require a version 1.X jar in your classpath.