From d1b7f7c3180fb227c1f10a455055b9d004bc95f9 Mon Sep 17 00:00:00 2001
From: Yuri Shkuro <github@ysh.us>
Date: Mon, 14 Sep 2020 14:33:48 -0400
Subject: [PATCH 1/3] Do not strip leading zeros from trace IDs

Signed-off-by: Yuri Shkuro <github@ysh.us>
---
 .../internal/JaegerSpanContext.java           | 22 ++++++++++---------
 .../internal/propagation/TextMapCodec.java    | 12 ++++++++--
 .../internal/JaegerSpanTest.java              |  8 +++++--
 .../propagation/TextMapCodecTest.java         |  5 +++--
 .../propagation/TraceContextCodecTest.java    |  6 ++---
 .../zipkin/internal/V2SpanConverterTest.java  |  5 ++++-
 6 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java b/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java
index 1ee3e0721..7fcb19fa0 100644
--- a/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java
+++ b/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java
@@ -71,7 +71,7 @@ protected JaegerSpanContext(
     this.debugId = debugId;
     this.objectFactory = objectFactory;
     this.traceIdAsString = convertTraceId();
-    this.spanIdAsString = Long.toHexString(spanId);
+    this.spanIdAsString = toHexString(spanId);
   }
 
   @Override
@@ -89,19 +89,21 @@ Map<String, String> baggage() {
 
   private String convertTraceId() {
     if (traceIdHigh == 0L) {
-      return Long.toHexString(traceIdLow);
-    }
-    final String hexStringHigh = Long.toHexString(traceIdHigh);
-    final String hexStringLow = Long.toHexString(traceIdLow);
-    if (hexStringLow.length() < 16) {
-      // left pad low trace id with '0'.
-      // In theory, only 12.5% of all possible long values will be padded.
-      // In practice, using Random.nextLong(), only 6% will need padding
-      return hexStringHigh + "0000000000000000".substring(hexStringLow.length()) + hexStringLow;
+      return toHexString(traceIdLow);
     }
+    final String hexStringHigh = toHexString(traceIdHigh);
+    final String hexStringLow = toHexString(traceIdLow);
     return hexStringHigh + hexStringLow;
   }
 
+  private static String toHexString(long id) {
+    final String hex = Long.toHexString(id);
+    if (hex.length() == 16) {
+      return hex;
+    }
+    return "0000000000000000".substring(hex.length()) + hex;
+  }
+
   public String getTraceId() {
     return this.traceIdAsString;
   }
diff --git a/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java b/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java
index 194259cab..a95c64f40 100644
--- a/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java
+++ b/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java
@@ -118,12 +118,20 @@ public static String contextAsString(JaegerSpanContext context) {
     int intFlag = context.getFlags() & 0xFF;
     return new StringBuilder()
         .append(context.getTraceId()).append(":")
-        .append(Long.toHexString(context.getSpanId())).append(":")
-        .append(Long.toHexString(context.getParentId())).append(":")
+        .append(toHexString(context.getSpanId())).append(":")
+        .append(toHexString(context.getParentId())).append(":")
         .append(Integer.toHexString(intFlag))
         .toString();
   }
 
+  private static String toHexString(long id) {
+    final String hex = Long.toHexString(id);
+    if (hex.length() == 16) {
+      return hex;
+    }
+    return "0000000000000000".substring(hex.length()) + hex;
+  }
+
   @Override
   public void inject(JaegerSpanContext spanContext, TextMap carrier) {
     carrier.put(contextKey, contextAsString(spanContext));
diff --git a/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java b/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java
index 15d7fc82d..56ab2dccd 100644
--- a/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java
+++ b/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java
@@ -295,7 +295,9 @@ public void testSpanToString() {
         false,
         Collections.emptyMap(),
         Collections.emptyList());
-    assertEquals("1:2:3:4 - test-operation", span.toString());
+    assertEquals(
+        "0000000000000001:0000000000000002:0000000000000003:4 - test-operation",
+        span.toString());
     span.finish();
   }
 
@@ -317,7 +319,9 @@ public void testSpanToStringWith128BitTraceId() {
         false,
         Collections.emptyMap(),
         Collections.emptyList());
-    assertEquals("20000000000000001:3:4:4 - test-operation", span.toString());
+    assertEquals(
+        "00000000000000020000000000000001:0000000000000003:0000000000000004:4 - test-operation",
+        span.toString());
     span.finish();
   }
 
diff --git a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java
index f993c5449..4fdf49dab 100644
--- a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java
+++ b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java
@@ -104,7 +104,8 @@ public void testContextAsStringWith128BitTraceId() {
 
     JaegerSpanContext context = new JaegerSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags);
     assertEquals(
-            "20000000000000001:3:4:5", TextMapCodec.contextAsString(context));
+            "00000000000000020000000000000001:0000000000000003:0000000000000004:5",
+            TextMapCodec.contextAsString(context));
   }
 
   @Test
@@ -169,7 +170,7 @@ public void testInjectDoNotEncodeSpanContext() {
     codec.inject(new JaegerSpanContext(0L, traceIdLow, spanId, parentId, (byte)1), new TextMapAdapter(headers));
 
     String traceId = headers.get("uber-trace-id");
-    assertEquals("2a:1:0:1", traceId);
+    assertEquals("000000000000002a:0000000000000001:0000000000000000:1", traceId);
   }
 
   @Test
diff --git a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java
index 71dcb690d..4147c3d17 100644
--- a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java
+++ b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java
@@ -113,7 +113,7 @@ public void testInjectWith64bit() {
     String traceParent = carrier.get(TRACE_PARENT);
     assertEquals(EXAMPLE_TRACE_PARENT, traceParent);
     JaegerSpanContext extractedContext = traceContextCodec.extract(textMap);
-    assertEquals("1:2:0:0", extractedContext.toString());
+    assertEquals("0000000000000001:0000000000000002:0000000000000000:0", extractedContext.toString());
   }
 
   @Test
@@ -123,7 +123,7 @@ public void testExtractWithCapitalizedTraceHeaders() {
     textMap.put("Traceparent", EXAMPLE_TRACE_PARENT);
     textMap.put("Tracestate", "whatever");
     JaegerSpanContext spanContext = traceContextCodec.extract(textMap);
-    assertEquals("1:2:0:0", spanContext.toString());
+    assertEquals("0000000000000001:0000000000000002:0000000000000000:0", spanContext.toString());
     assertEquals("whatever", spanContext.getTraceState());
   }
 
@@ -201,7 +201,7 @@ public void testDebugIdWithTraceHeader() {
     textMap.put(Constants.DEBUG_ID_HEADER_KEY, EXAMPLE_DEBUG_ID);
     JaegerSpanContext spanContext = traceContextCodec.extract(textMap);
     JaegerTracer tracer = new JaegerTracer.Builder("service").withReporter(new InMemoryReporter()).build();
-    assertEquals("1", spanContext.getTraceId());
+    assertEquals("0000000000000001", spanContext.getTraceId());
     JaegerSpan child = tracer.buildSpan("span").asChildOf(spanContext).start();
     assertFalse(child.context().isDebug());
     child.finish();
diff --git a/jaeger-zipkin/src/test/java/io/jaegertracing/zipkin/internal/V2SpanConverterTest.java b/jaeger-zipkin/src/test/java/io/jaegertracing/zipkin/internal/V2SpanConverterTest.java
index 4c77b63c4..90f2fb513 100644
--- a/jaeger-zipkin/src/test/java/io/jaegertracing/zipkin/internal/V2SpanConverterTest.java
+++ b/jaeger-zipkin/src/test/java/io/jaegertracing/zipkin/internal/V2SpanConverterTest.java
@@ -303,7 +303,10 @@ public void testSpanLogsCreateAnnotations() {
 
   @Test
   public void testConvertSpanWith128BitTraceId() {
-    JaegerSpan span = tracer128.buildSpan("operation-name").start();
+    // zipkinSpan.traceId() always returns full length ID (padded with 0s).
+    // To make the test stable, use a short idHigh portion.
+    JaegerSpanContext c = new JaegerSpanContext(1L, 1L, 1L, 0L, (byte)0x01);
+    JaegerSpan span = tracer128.buildSpan("operation-name").asChildOf(c).start();
 
     zipkin2.Span zipkinSpan = V2SpanConverter.convertSpan(span);
     assertNotEquals(0, span.context().getTraceIdHigh());

From 8f4435f85800c032e3149da47395474c6616952a Mon Sep 17 00:00:00 2001
From: Yuri Shkuro <github@ysh.us>
Date: Mon, 14 Sep 2020 14:42:34 -0400
Subject: [PATCH 2/3] Reuse util function

Signed-off-by: Yuri Shkuro <github@ysh.us>
---
 .../internal/JaegerSpanContext.java             | 17 +++++------------
 .../internal/propagation/TextMapCodec.java      | 14 ++++----------
 .../io/jaegertracing/internal/utils/Utils.java  |  8 ++++++++
 .../internal/propagation/TextMapCodecTest.java  |  2 +-
 .../propagation/TraceContextCodecTest.java      |  4 ++--
 5 files changed, 20 insertions(+), 25 deletions(-)

diff --git a/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java b/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java
index 7fcb19fa0..1c12afcea 100644
--- a/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java
+++ b/jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java
@@ -16,6 +16,7 @@
 package io.jaegertracing.internal;
 
 import io.jaegertracing.internal.propagation.TextMapCodec;
+import io.jaegertracing.internal.utils.Utils;
 import io.opentracing.SpanContext;
 
 import java.util.Collections;
@@ -71,7 +72,7 @@ protected JaegerSpanContext(
     this.debugId = debugId;
     this.objectFactory = objectFactory;
     this.traceIdAsString = convertTraceId();
-    this.spanIdAsString = toHexString(spanId);
+    this.spanIdAsString = Utils.to16HexString(spanId);
   }
 
   @Override
@@ -89,21 +90,13 @@ Map<String, String> baggage() {
 
   private String convertTraceId() {
     if (traceIdHigh == 0L) {
-      return toHexString(traceIdLow);
+      return Utils.to16HexString(traceIdLow);
     }
-    final String hexStringHigh = toHexString(traceIdHigh);
-    final String hexStringLow = toHexString(traceIdLow);
+    final String hexStringHigh = Utils.to16HexString(traceIdHigh);
+    final String hexStringLow = Utils.to16HexString(traceIdLow);
     return hexStringHigh + hexStringLow;
   }
 
-  private static String toHexString(long id) {
-    final String hex = Long.toHexString(id);
-    if (hex.length() == 16) {
-      return hex;
-    }
-    return "0000000000000000".substring(hex.length()) + hex;
-  }
-
   public String getTraceId() {
     return this.traceIdAsString;
   }
diff --git a/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java b/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java
index a95c64f40..f42a8f687 100644
--- a/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java
+++ b/jaeger-core/src/main/java/io/jaegertracing/internal/propagation/TextMapCodec.java
@@ -21,6 +21,7 @@
 import io.jaegertracing.internal.exceptions.EmptyTracerStateStringException;
 import io.jaegertracing.internal.exceptions.MalformedTracerStateStringException;
 import io.jaegertracing.internal.exceptions.TraceIdOutOfBoundException;
+import io.jaegertracing.internal.utils.Utils;
 import io.jaegertracing.spi.Codec;
 import io.opentracing.propagation.TextMap;
 
@@ -118,20 +119,13 @@ public static String contextAsString(JaegerSpanContext context) {
     int intFlag = context.getFlags() & 0xFF;
     return new StringBuilder()
         .append(context.getTraceId()).append(":")
-        .append(toHexString(context.getSpanId())).append(":")
-        .append(toHexString(context.getParentId())).append(":")
+        .append(Utils.to16HexString(context.getSpanId())).append(":")
+        // parent=0 is special, no need to encode as full 16 characters, and more readable this way
+        .append(context.getParentId() == 0 ? "0" : Utils.to16HexString(context.getParentId())).append(":")
         .append(Integer.toHexString(intFlag))
         .toString();
   }
 
-  private static String toHexString(long id) {
-    final String hex = Long.toHexString(id);
-    if (hex.length() == 16) {
-      return hex;
-    }
-    return "0000000000000000".substring(hex.length()) + hex;
-  }
-
   @Override
   public void inject(JaegerSpanContext spanContext, TextMap carrier) {
     carrier.put(contextKey, contextAsString(spanContext));
diff --git a/jaeger-core/src/main/java/io/jaegertracing/internal/utils/Utils.java b/jaeger-core/src/main/java/io/jaegertracing/internal/utils/Utils.java
index 107738275..bbd39ce27 100644
--- a/jaeger-core/src/main/java/io/jaegertracing/internal/utils/Utils.java
+++ b/jaeger-core/src/main/java/io/jaegertracing/internal/utils/Utils.java
@@ -60,5 +60,13 @@ public static boolean equals(Object a, Object b) {
     return (a == b) || (a != null && a.equals(b));
   }
 
+  public static String to16HexString(long id) {
+    final String hex = Long.toHexString(id);
+    if (hex.length() == 16) {
+      return hex;
+    }
+    return "0000000000000000".substring(hex.length()) + hex;
+  }
+
   private Utils() {}
 }
diff --git a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java
index 4fdf49dab..93bd6e960 100644
--- a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java
+++ b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TextMapCodecTest.java
@@ -170,7 +170,7 @@ public void testInjectDoNotEncodeSpanContext() {
     codec.inject(new JaegerSpanContext(0L, traceIdLow, spanId, parentId, (byte)1), new TextMapAdapter(headers));
 
     String traceId = headers.get("uber-trace-id");
-    assertEquals("000000000000002a:0000000000000001:0000000000000000:1", traceId);
+    assertEquals("000000000000002a:0000000000000001:0:1", traceId);
   }
 
   @Test
diff --git a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java
index 4147c3d17..eb2e7cdaf 100644
--- a/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java
+++ b/jaeger-core/src/test/java/io/jaegertracing/internal/propagation/TraceContextCodecTest.java
@@ -113,7 +113,7 @@ public void testInjectWith64bit() {
     String traceParent = carrier.get(TRACE_PARENT);
     assertEquals(EXAMPLE_TRACE_PARENT, traceParent);
     JaegerSpanContext extractedContext = traceContextCodec.extract(textMap);
-    assertEquals("0000000000000001:0000000000000002:0000000000000000:0", extractedContext.toString());
+    assertEquals("0000000000000001:0000000000000002:0:0", extractedContext.toString());
   }
 
   @Test
@@ -123,7 +123,7 @@ public void testExtractWithCapitalizedTraceHeaders() {
     textMap.put("Traceparent", EXAMPLE_TRACE_PARENT);
     textMap.put("Tracestate", "whatever");
     JaegerSpanContext spanContext = traceContextCodec.extract(textMap);
-    assertEquals("0000000000000001:0000000000000002:0000000000000000:0", spanContext.toString());
+    assertEquals("0000000000000001:0000000000000002:0:0", spanContext.toString());
     assertEquals("whatever", spanContext.getTraceState());
   }
 

From b92510eb2173d4ac1b2cc31adaa34185f5edf1de Mon Sep 17 00:00:00 2001
From: Yuri Shkuro <github@ysh.us>
Date: Mon, 14 Sep 2020 14:45:48 -0400
Subject: [PATCH 3/3] fix

Signed-off-by: Yuri Shkuro <github@ysh.us>
---
 .../test/java/io/jaegertracing/internal/JaegerSpanTest.java    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java b/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java
index 56ab2dccd..0996ec87e 100644
--- a/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java
+++ b/jaeger-core/src/test/java/io/jaegertracing/internal/JaegerSpanTest.java
@@ -31,6 +31,7 @@
 import io.jaegertracing.internal.metrics.Metrics;
 import io.jaegertracing.internal.reporters.InMemoryReporter;
 import io.jaegertracing.internal.samplers.ConstSampler;
+import io.jaegertracing.internal.utils.Utils;
 import io.jaegertracing.spi.BaggageRestrictionManager;
 import io.opentracing.References;
 import io.opentracing.Span;
@@ -190,7 +191,7 @@ public void testToTraceId() {
 
   @Test
   public void testToSpanId() {
-    assertEquals(Long.toHexString(jaegerSpan.context().getSpanId()), jaegerSpan.context().toSpanId());
+    assertEquals(Utils.to16HexString(jaegerSpan.context().getSpanId()), jaegerSpan.context().toSpanId());
   }
 
   @Test