Howto Upgrade to Zipkin2

3 minute read


Java Chassis uses zipkin as the default tracing implementation.

Zipkin introduced v2 http api in version 1.31 which simplifies data types. There are also various other improvements and new features added to the zipkin libraries, so it seems a good time for us to follow the upstream and upgrade to zipkin2.

Version matrix

module current target supports v2 since
zipkin 1.24.0 2.4.2 2.0.0
brave 4.13.1 4.13.1 4.7.1
reporter 0.10.0 2.2.2 2.0.0

What’s changed

Zipkin did a very good job on maintaining backward compatibility. All the changes that breaks compatiblity are packaged into a new group( -> io.zipkin.zipkin2, io.zipkin.reporter -> io.zipkin.reporter2). And v1/v2 libraries can coexist.

The zipkin2 library can use both v1 and v2 api to communicate with server.

The zipkin2.Span class changed a bit from the old zipkin.Span class. The public fields are refactored to methods. And the BinaryAnnotation class is removed along with zipkin.Span.binaryAnnotaions field. It’s functionality is replaced by zipkin2.Span.tags() method which return a Map<String,String>.

Upgrade to zipkin2 for Java Chassis

Change the group and io.zipkin.reporter to io.zipkin.zipkin2 and io.zipkin.reporter2 respectively.

@@ -50,8 +50,8 @@
-    <zipkin.version>1.24.0</zipkin.version>
-    <zipkin-reporter.version>0.10.0</zipkin-reporter.version>
+    <zipkin.version>2.4.2</zipkin.version>
+    <zipkin-reporter.version>2.2.2</zipkin-reporter.version>
@@ -646,7 +646,7 @@
       <!-- zipkin dependencies -->
-        <groupId></groupId>
+        <groupId>io.zipkin.zipkin2</groupId>
@@ -661,7 +661,7 @@
-        <groupId>io.zipkin.reporter</groupId>
+        <groupId>io.zipkin.reporter2</groupId>

@@ -50,7 +50,7 @@
-      <groupId>io.zipkin.reporter</groupId>
+      <groupId>io.zipkin.reporter2</groupId>

Make brave to use zipkin2 instead of zipkin

Change the import to on imports, and most importantly, use spanReporter() instead of reporter() for generating reporter for brave, change the api path to /api/v2/xxx when creating sender.

@@ -31,11 +31,11 @@
 import brave.http.HttpTracing;
 import brave.propagation.CurrentTraceContext;
 import org.apache.servicecomb.config.DynamicProperties;
-import zipkin.Span;
-import zipkin.reporter.AsyncReporter;
-import zipkin.reporter.Reporter;
-import zipkin.reporter.Sender;
-import zipkin.reporter.okhttp3.OkHttpSender;
+import zipkin2.Span;
+import zipkin2.reporter.AsyncReporter;
+import zipkin2.reporter.Reporter;
+import zipkin2.reporter.Sender;
+import zipkin2.reporter.okhttp3.OkHttpSender;
 class TracingConfiguration {
@@ -56,14 +56,15 @@ Sender sender(DynamicProperties dynamicProperties) {
     return AsyncReporter.builder(sender).build();
   Tracing tracing(Reporter<Span> reporter, DynamicProperties dynamicProperties,
       CurrentTraceContext currentTraceContext) {
     return Tracing.newBuilder()
         .currentTraceContext(currentTraceContext) // puts trace IDs into logs
-        .reporter(reporter)
+        .spanReporter(reporter)


In the brave release notes, it’s stated that we need to use create() instead of builder()

   /** 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.create(sender()).build();

But this will not work. In zipkin2.Reporter, the create(sender) is actually equivalent to builder(sender).build()

  public static AsyncReporter<Span> create(Sender sender) {
    return (new AsyncReporter.Builder(sender)).build();
  public static AsyncReporter.Builder builder(Sender sender) {
    return new AsyncReporter.Builder(sender);

Make changes according to the changes of zipkin.Span and zipkin2.Span.

We do not use zipkin.Span in our production code, but we do use it in our tests. Those changes are quite straight forward, we just change the accessing of fields to calling methods as described in the What’s Changed section.

@@ -45,7 +45,7 @@
-import zipkin.Span;
+import zipkin2.Span;
 @SpringBootTest(classes = {ZipkinSpanTestApplication.class, TracingConfig.class})
@@ -74,8 +74,8 @@ public void reportedSpanContainsAnnotatedMethodInfo() throws Exception {
     await().atMost(2, SECONDS).until(() -> !spans.isEmpty());
-    zipkin.Span span = spans.poll();
-    assertThat(, is("crawl"));
+    zipkin2.Span span = spans.poll();
+    assertThat(, is("crawl"));
     assertThat(tracedValues(span), contains(SomeSlowTask.class.getMethod("crawl").toString()));
@@ -84,17 +84,17 @@ public void reportCustomSpanInfomation() throws Exception {
     await().atMost(2, SECONDS).until(() -> !spans.isEmpty());
-    zipkin.Span span = spans.poll();
-    assertThat(, is("transaction1"));
+    zipkin2.Span span = spans.poll();
+    assertThat(, is("transaction1"));
     assertThat(tracedValues(span), contains("startA"));
-  private List<String> tracedValues(zipkin.Span spans) {
-    return
-        .filter(span -> CALL_PATH.equals(span.key) || "error".equals(span.key))
-        .filter(span -> span.value != null)
-        .map(annotation -> new String(annotation.value))
+  private List<String> tracedValues(zipkin2.Span spans) {
+    return spans.tags().entrySet().stream()
+        .filter(span -> CALL_PATH.equals(span.getKey()) || "error".equals(span.getKey()))
+        .filter(span -> span.getValue() != null)
+        .map(annotation -> new String(annotation.getValue()))
@@ -110,7 +110,7 @@ public void reportCustomSpanInfomation() throws Exception {
     Tracing tracing(Queue<Span> spans) {
       return Tracing.newBuilder()
           .currentTraceContext(new StrictCurrentTraceContext())
-          .reporter(spans::add)
+          .spanReporter(spans::add)

Support both v1 and v2 api of zipkin server.

Our customers may be still running a zipkin server prior to 1.31 which does not support the v2 http api. So we added an option to let them specify the server api version.

Supporting v1 api is built into zipkin2, so we do not need to rely on the v1 libraries. Just use the SpanBytesEncoder.JSON_V1 when building reporter and change the sender api path.

sender = URLConnectionSender.create("http://localhost:9411/api/v1/spans")
reporter = AsyncReporter.builder(sender)

That’s all, for the complete changes, please refer the pull request for the complete changes.




Leave a Comment

Your email address will not be published. Required fields are marked *