Skip to content
This repository was archived by the owner on Jul 1, 2022. It is now read-only.

Commit 88a8497

Browse files
hypnocejpkrohling
authored andcommitted
Fixes #495 : support for 128 bits trace ids. (#507)
Added environment variable JAEGER_TRACEID_128BIT Added withTraceId128Bit method to Configuration Added withTraceId128Bit method to Tracer.Builder Added useTraceId128Bit property to Tracer Signed-off-by: JACQUES Francois <fjacques@murex.com>
1 parent 6ee913a commit 88a8497

File tree

24 files changed

+482
-94
lines changed

24 files changed

+482
-94
lines changed

jaeger-core/src/main/java/io/jaegertracing/Configuration.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ public class Configuration {
147147
*/
148148
public static final String JAEGER_SENDER_FACTORY = JAEGER_PREFIX + "SENDER_FACTORY";
149149

150+
/**
151+
* Opt-in to use 128 bit traceIds. By default, uses 64 bits.
152+
*/
153+
public static final String JAEGER_TRACEID_128BIT = JAEGER_PREFIX + "TRACEID_128BIT";
154+
150155
/**
151156
* The supported trace context propagation formats.
152157
*/
@@ -172,6 +177,7 @@ public enum Propagation {
172177
private CodecConfiguration codecConfig;
173178
private MetricsFactory metricsFactory;
174179
private Map<String, String> tracerTags;
180+
private boolean useTraceId128Bit;
175181

176182
/**
177183
* lazy singleton JaegerTracer initialized in getTracer() method.
@@ -193,6 +199,7 @@ public static Configuration fromEnv() {
193199
public static Configuration fromEnv(String serviceName) {
194200
return new Configuration(serviceName)
195201
.withTracerTags(tracerTagsFromEnv())
202+
.withTraceId128Bit(getPropertyAsBool(JAEGER_TRACEID_128BIT))
196203
.withReporter(ReporterConfiguration.fromEnv())
197204
.withSampler(SamplerConfiguration.fromEnv())
198205
.withCodec(CodecConfiguration.fromEnv());
@@ -219,6 +226,9 @@ public JaegerTracer.Builder getTracerBuilder() {
219226
.withReporter(reporter)
220227
.withMetrics(metrics)
221228
.withTags(tracerTags);
229+
if (useTraceId128Bit) {
230+
builder = builder.withTraceId128Bit();
231+
}
222232
codecConfig.apply(builder);
223233
return builder;
224234
}
@@ -285,6 +295,11 @@ public Configuration withCodec(CodecConfiguration codecConfig) {
285295
return this;
286296
}
287297

298+
public Configuration withTraceId128Bit(boolean useTraceId128Bit) {
299+
this.useTraceId128Bit = useTraceId128Bit;
300+
return this;
301+
}
302+
288303
public Configuration withTracerTags(Map<String, String> tracerTags) {
289304
if (tracerTags != null) {
290305
this.tracerTags = new HashMap<String, String>(tracerTags);

jaeger-core/src/main/java/io/jaegertracing/internal/JaegerObjectFactory.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,14 @@ public JaegerSpan createSpan(
5757
}
5858

5959
public JaegerSpanContext createSpanContext(
60-
long traceId,
60+
long traceIdHigh,
61+
long traceIdLow,
6162
long spanId,
6263
long parentId,
6364
byte flags,
6465
Map<String, String> baggage,
6566
String debugId) {
66-
return new JaegerSpanContext(traceId, spanId, parentId, flags, baggage, debugId, this);
67+
return new JaegerSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, baggage, debugId, this);
6768
}
6869

6970
public JaegerTracer.SpanBuilder createSpanBuilder(JaegerTracer tracer, String operationName) {

jaeger-core/src/main/java/io/jaegertracing/internal/JaegerSpanContext.java

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import io.jaegertracing.internal.propagation.TextMapCodec;
1919
import io.opentracing.SpanContext;
20+
2021
import java.util.Collections;
2122
import java.util.HashMap;
2223
import java.util.Map;
@@ -25,17 +26,19 @@ public class JaegerSpanContext implements SpanContext {
2526
protected static final byte flagSampled = 1;
2627
protected static final byte flagDebug = 2;
2728

28-
private final long traceId;
29+
private final long traceIdLow;
30+
private final long traceIdHigh;
2931
private final long spanId;
3032
private final long parentId;
3133
private final byte flags;
3234
private final Map<String, String> baggage;
3335
private final String debugId;
3436
private final JaegerObjectFactory objectFactory;
3537

36-
public JaegerSpanContext(long traceId, long spanId, long parentId, byte flags) {
38+
public JaegerSpanContext(long traceIdHigh, long traceIdLow, long spanId, long parentId, byte flags) {
3739
this(
38-
traceId,
40+
traceIdHigh,
41+
traceIdLow,
3942
spanId,
4043
parentId,
4144
flags,
@@ -45,7 +48,8 @@ public JaegerSpanContext(long traceId, long spanId, long parentId, byte flags) {
4548
}
4649

4750
protected JaegerSpanContext(
48-
long traceId,
51+
long traceIdHigh,
52+
long traceIdLow,
4953
long spanId,
5054
long parentId,
5155
byte flags,
@@ -55,7 +59,8 @@ protected JaegerSpanContext(
5559
if (baggage == null) {
5660
baggage = Collections.<String, String>emptyMap();
5761
}
58-
this.traceId = traceId;
62+
this.traceIdLow = traceIdLow;
63+
this.traceIdHigh = traceIdHigh;
5964
this.spanId = spanId;
6065
this.parentId = parentId;
6166
this.flags = flags;
@@ -77,8 +82,27 @@ Map<String, String> baggage() {
7782
return this.baggage;
7883
}
7984

80-
public long getTraceId() {
81-
return traceId;
85+
public String getTraceId() {
86+
if (traceIdHigh == 0L) {
87+
return Long.toHexString(traceIdLow);
88+
}
89+
final String hexStringHigh = Long.toHexString(traceIdHigh);
90+
final String hexStringLow = Long.toHexString(traceIdLow);
91+
if (hexStringLow.length() < 16) {
92+
// left pad low trace id with '0'.
93+
// In theory, only 12.5% of all possible long values will be padded.
94+
// In practice, using Random.nextLong(), only 6% will need padding
95+
return hexStringHigh + "0000000000000000".substring(hexStringLow.length()) + hexStringLow;
96+
}
97+
return hexStringHigh + hexStringLow;
98+
}
99+
100+
public long getTraceIdLow() {
101+
return traceIdLow;
102+
}
103+
104+
public long getTraceIdHigh() {
105+
return traceIdHigh;
82106
}
83107

84108
public long getSpanId() {
@@ -113,15 +137,15 @@ public JaegerSpanContext withBaggageItem(String key, String val) {
113137
} else {
114138
newBaggage.put(key, val);
115139
}
116-
return objectFactory.createSpanContext(traceId, spanId, parentId, flags, newBaggage, debugId);
140+
return objectFactory.createSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, newBaggage, debugId);
117141
}
118142

119143
public JaegerSpanContext withBaggage(Map<String, String> newBaggage) {
120-
return objectFactory.createSpanContext(traceId, spanId, parentId, flags, newBaggage, debugId);
144+
return objectFactory.createSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, newBaggage, debugId);
121145
}
122146

123147
public JaegerSpanContext withFlags(byte flags) {
124-
return objectFactory.createSpanContext(traceId, spanId, parentId, flags, baggage, debugId);
148+
return objectFactory.createSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, baggage, debugId);
125149
}
126150

127151
/**
@@ -133,7 +157,7 @@ public JaegerSpanContext withFlags(byte flags) {
133157
* @see Constants#BAGGAGE_HEADER_KEY
134158
*/
135159
boolean hasTrace() {
136-
return traceId != 0 && spanId != 0;
160+
return (traceIdLow != 0 || traceIdHigh != 0) && spanId != 0;
137161
}
138162

139163
/**

jaeger-core/src/main/java/io/jaegertracing/internal/JaegerTracer.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ public class JaegerTracer implements Tracer, Closeable {
6868
private final Map<String, ?> tags;
6969
private final boolean zipkinSharedRpcSpan;
7070
private final boolean expandExceptionLogs;
71+
private final boolean useTraceId128Bit;
7172

7273
@ToString.Exclude private final PropagationRegistry registry;
7374
@ToString.Exclude private final Clock clock;
@@ -89,6 +90,7 @@ protected JaegerTracer(JaegerTracer.Builder builder) {
8990
this.baggageSetter = new BaggageSetter(builder.baggageRestrictionManager, metrics);
9091
this.expandExceptionLogs = builder.expandExceptionLogs;
9192
this.objectFactory = builder.objectFactory;
93+
this.useTraceId128Bit = builder.useTraceId128Bit;
9294

9395
this.version = loadVersion();
9496

@@ -299,7 +301,9 @@ public JaegerTracer.SpanBuilder withStartTimestamp(long microseconds) {
299301

300302
private JaegerSpanContext createNewContext() {
301303
String debugId = getDebugId();
302-
long id = Utils.uniqueId();
304+
long spanId = Utils.uniqueId();
305+
long traceIdLow = spanId;
306+
long traceIdHigh = isUseTraceId128Bit() ? Utils.uniqueId() : 0;
303307

304308
byte flags = 0;
305309
if (debugId != null) {
@@ -308,7 +312,7 @@ private JaegerSpanContext createNewContext() {
308312
metrics.traceStartedSampled.inc(1);
309313
} else {
310314
// TODO: (prithvi) Don't assume operationName is set on creation
311-
SamplingStatus samplingStatus = sampler.sample(operationName, id);
315+
SamplingStatus samplingStatus = sampler.sample(operationName, spanId);
312316
if (samplingStatus.isSampled()) {
313317
flags |= JaegerSpanContext.flagSampled;
314318
tags.putAll(samplingStatus.getTags());
@@ -319,8 +323,9 @@ private JaegerSpanContext createNewContext() {
319323
}
320324

321325
return getObjectFactory().createSpanContext(
322-
id,
323-
id,
326+
traceIdHigh,
327+
traceIdLow,
328+
spanId,
324329
0,
325330
flags,
326331
getBaggage(),
@@ -364,7 +369,8 @@ private JaegerSpanContext createChildContext() {
364369
}
365370

366371
return getObjectFactory().createSpanContext(
367-
preferredReference.getTraceId(),
372+
preferredReference.getTraceIdHigh(),
373+
preferredReference.getTraceIdLow(),
368374
Utils.uniqueId(),
369375
preferredReference.getSpanId(),
370376
// should we do OR across passed references?
@@ -490,6 +496,7 @@ public static class Builder {
490496
private BaggageRestrictionManager baggageRestrictionManager = new DefaultBaggageRestrictionManager();
491497
private boolean expandExceptionLogs;
492498
private final JaegerObjectFactory objectFactory;
499+
private boolean useTraceId128Bit;
493500

494501
public Builder(String serviceName) {
495502
this(serviceName, new JaegerObjectFactory());
@@ -577,6 +584,11 @@ public Builder withMetrics(Metrics metrics) {
577584
return this;
578585
}
579586

587+
public Builder withTraceId128Bit() {
588+
this.useTraceId128Bit = true;
589+
return this;
590+
}
591+
580592
public Builder withTag(String key, String value) {
581593
tags.put(key, value);
582594
return this;
@@ -670,4 +682,8 @@ JaegerSpanContext setBaggage(JaegerSpan jaegerSpan, String key, String value) {
670682
boolean isExpandExceptionLogs() {
671683
return this.expandExceptionLogs;
672684
}
685+
686+
public boolean isUseTraceId128Bit() {
687+
return this.useTraceId128Bit;
688+
}
673689
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2018, The Jaeger Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
15+
package io.jaegertracing.internal.exceptions;
16+
17+
public class TraceIdOutOfBoundException extends RuntimeException {
18+
19+
private static final long serialVersionUID = 2332452744805504972L;
20+
21+
public TraceIdOutOfBoundException(String message) {
22+
super(message);
23+
}
24+
}

jaeger-core/src/main/java/io/jaegertracing/internal/propagation/B3TextMapCodec.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ private B3TextMapCodec(Builder builder) {
7777

7878
@Override
7979
public void inject(JaegerSpanContext spanContext, TextMap carrier) {
80-
carrier.put(TRACE_ID_NAME, HexCodec.toLowerHex(spanContext.getTraceId()));
80+
carrier.put(TRACE_ID_NAME, // Use HexCode instead of getTraceId to ensure zipkin compatibility
81+
HexCodec.toLowerHex(spanContext.getTraceIdHigh(), spanContext.getTraceIdLow()));
8182
if (spanContext.getParentId() != 0L) { // Conventionally, parent id == 0 means the root span
8283
carrier.put(PARENT_SPAN_ID_NAME, HexCodec.toLowerHex(spanContext.getParentId()));
8384
}
@@ -93,7 +94,8 @@ public void inject(JaegerSpanContext spanContext, TextMap carrier) {
9394

9495
@Override
9596
public JaegerSpanContext extract(TextMap carrier) {
96-
Long traceId = null;
97+
Long traceIdLow = null;
98+
Long traceIdHigh = 0L; // It's enough to check for a null low trace id
9799
Long spanId = null;
98100
Long parentId = 0L; // Conventionally, parent id == 0 means the root span
99101
byte flags = 0;
@@ -105,7 +107,8 @@ public JaegerSpanContext extract(TextMap carrier) {
105107
flags |= SAMPLED_FLAG;
106108
}
107109
} else if (entry.getKey().equalsIgnoreCase(TRACE_ID_NAME)) {
108-
traceId = HexCodec.lowerHexToUnsignedLong(entry.getValue());
110+
traceIdLow = HexCodec.lowerHexToUnsignedLong(entry.getValue());
111+
traceIdHigh = HexCodec.higherHexToUnsignedLong(entry.getValue());
109112
} else if (entry.getKey().equalsIgnoreCase(PARENT_SPAN_ID_NAME)) {
110113
parentId = HexCodec.lowerHexToUnsignedLong(entry.getValue());
111114
} else if (entry.getKey().equalsIgnoreCase(SPAN_ID_NAME)) {
@@ -122,9 +125,10 @@ public JaegerSpanContext extract(TextMap carrier) {
122125
}
123126
}
124127

125-
if (null != traceId && null != parentId && null != spanId) {
128+
if (null != traceIdLow && null != parentId && null != spanId) {
126129
JaegerSpanContext spanContext = objectFactory.createSpanContext(
127-
traceId,
130+
traceIdHigh,
131+
traceIdLow,
128132
spanId,
129133
parentId,
130134
flags,

jaeger-core/src/main/java/io/jaegertracing/internal/propagation/HexCodec.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414

1515
package io.jaegertracing.internal.propagation;
1616

17+
import lombok.extern.slf4j.Slf4j;
18+
1719
// copy/pasted from brave.internal.HexCodec 4.1.1 to avoid build complexity
20+
@Slf4j
1821
final class HexCodec {
1922

2023
/**
@@ -26,13 +29,30 @@ final class HexCodec {
2629
static Long lowerHexToUnsignedLong(String lowerHex) {
2730
int length = lowerHex.length();
2831
if (length < 1 || length > 32) {
32+
log.debug("token {} size is out of bounds [1, 32]", lowerHex);
2933
return null;
3034
}
3135

3236
// trim off any high bits
3337
int beginIndex = length > 16 ? length - 16 : 0;
3438

35-
return lowerHexToUnsignedLong(lowerHex, beginIndex);
39+
return hexToUnsignedLong(lowerHex, beginIndex, Math.min(beginIndex + 16, lowerHex.length()));
40+
}
41+
42+
/**
43+
* Parses a 1 to 32 character higher-hex string with no prefix into an unsigned long, tossing any
44+
* bits lower than 64.
45+
*
46+
* @return a 64 bit long, meaning that negative values are the overflow of Java's 32 bit long
47+
*/
48+
static Long higherHexToUnsignedLong(String higherHex) {
49+
int length = higherHex.length();
50+
if (length > 32 || length < 1) {
51+
log.debug("token {} size is out of bounds [1, 32]", higherHex);
52+
return null;
53+
}
54+
55+
return hexToUnsignedLong(higherHex, 0, Math.max(length - 16, 0));
3656
}
3757

3858
/**
@@ -41,9 +61,9 @@ static Long lowerHexToUnsignedLong(String lowerHex) {
4161
*
4262
* @return a 64 bit long, meaning that negative values are the overflow of Java's 32 bit long
4363
*/
44-
static Long lowerHexToUnsignedLong(String lowerHex, int index) {
64+
static Long hexToUnsignedLong(String lowerHex, int index, int endIndex) {
4565
long result = 0;
46-
for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) {
66+
for (; index < endIndex; index++) {
4767
char c = lowerHex.charAt(index);
4868
result <<= 4;
4969
if (c >= '0' && c <= '9') {

0 commit comments

Comments
 (0)