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

Support for 128 bits trace ids #507

Merged
merged 1 commit into from
Oct 15, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions jaeger-core/src/main/java/io/jaegertracing/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ public class Configuration {
*/
public static final String JAEGER_SENDER_FACTORY = JAEGER_PREFIX + "SENDER_FACTORY";

/**
* Opt-in to use 128 bit traceIds. By default, uses 64 bits.
*/
public static final String JAEGER_TRACEID_128BIT = JAEGER_PREFIX + "TRACEID_128BIT";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be documented in readme

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is currently no structured documentation in the README for the env variable. The only thing the current documentation : jaegertracing/documentation#157
Do you want to create a place where all the env variable are listed and documented ?


/**
* The supported trace context propagation formats.
*/
Expand All @@ -172,6 +177,7 @@ public enum Propagation {
private CodecConfiguration codecConfig;
private MetricsFactory metricsFactory;
private Map<String, String> tracerTags;
private boolean useTraceId128Bit;

/**
* lazy singleton JaegerTracer initialized in getTracer() method.
Expand All @@ -193,6 +199,7 @@ public static Configuration fromEnv() {
public static Configuration fromEnv(String serviceName) {
return new Configuration(serviceName)
.withTracerTags(tracerTagsFromEnv())
.withTraceId128Bit(getPropertyAsBool(JAEGER_TRACEID_128BIT))
.withReporter(ReporterConfiguration.fromEnv())
.withSampler(SamplerConfiguration.fromEnv())
.withCodec(CodecConfiguration.fromEnv());
Expand All @@ -219,6 +226,9 @@ public JaegerTracer.Builder getTracerBuilder() {
.withReporter(reporter)
.withMetrics(metrics)
.withTags(tracerTags);
if (useTraceId128Bit) {
builder = builder.withTraceId128Bit();
}
codecConfig.apply(builder);
return builder;
}
Expand Down Expand Up @@ -285,6 +295,11 @@ public Configuration withCodec(CodecConfiguration codecConfig) {
return this;
}

public Configuration withTraceId128Bit(boolean useTraceId128Bit) {
this.useTraceId128Bit = useTraceId128Bit;
return this;
}

public Configuration withTracerTags(Map<String, String> tracerTags) {
if (tracerTags != null) {
this.tracerTags = new HashMap<String, String>(tracerTags);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,14 @@ public JaegerSpan createSpan(
}

public JaegerSpanContext createSpanContext(
long traceId,
long traceIdHigh,
long traceIdLow,
long spanId,
long parentId,
byte flags,
Map<String, String> baggage,
String debugId) {
return new JaegerSpanContext(traceId, spanId, parentId, flags, baggage, debugId, this);
return new JaegerSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, baggage, debugId, this);
}

public JaegerTracer.SpanBuilder createSpanBuilder(JaegerTracer tracer, String operationName) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import io.jaegertracing.internal.propagation.TextMapCodec;
import io.opentracing.SpanContext;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -25,17 +26,19 @@ public class JaegerSpanContext implements SpanContext {
protected static final byte flagSampled = 1;
protected static final byte flagDebug = 2;

private final long traceId;
private final long traceIdLow;
private final long traceIdHigh;
private final long spanId;
private final long parentId;
private final byte flags;
private final Map<String, String> baggage;
private final String debugId;
private final JaegerObjectFactory objectFactory;

public JaegerSpanContext(long traceId, long spanId, long parentId, byte flags) {
public JaegerSpanContext(long traceIdHigh, long traceIdLow, long spanId, long parentId, byte flags) {
this(
traceId,
traceIdHigh,
traceIdLow,
spanId,
parentId,
flags,
Expand All @@ -45,7 +48,8 @@ public JaegerSpanContext(long traceId, long spanId, long parentId, byte flags) {
}

protected JaegerSpanContext(
long traceId,
long traceIdHigh,
long traceIdLow,
long spanId,
long parentId,
byte flags,
Expand All @@ -55,7 +59,8 @@ protected JaegerSpanContext(
if (baggage == null) {
baggage = Collections.<String, String>emptyMap();
}
this.traceId = traceId;
this.traceIdLow = traceIdLow;
this.traceIdHigh = traceIdHigh;
this.spanId = spanId;
this.parentId = parentId;
this.flags = flags;
Expand All @@ -77,8 +82,27 @@ Map<String, String> baggage() {
return this.baggage;
}

public long getTraceId() {
return traceId;
public String getTraceId() {
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 hexStringHigh + hexStringLow;
}

public long getTraceIdLow() {
return traceIdLow;
}

public long getTraceIdHigh() {
return traceIdHigh;
}

public long getSpanId() {
Expand Down Expand Up @@ -113,15 +137,15 @@ public JaegerSpanContext withBaggageItem(String key, String val) {
} else {
newBaggage.put(key, val);
}
return objectFactory.createSpanContext(traceId, spanId, parentId, flags, newBaggage, debugId);
return objectFactory.createSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, newBaggage, debugId);
}

public JaegerSpanContext withBaggage(Map<String, String> newBaggage) {
return objectFactory.createSpanContext(traceId, spanId, parentId, flags, newBaggage, debugId);
return objectFactory.createSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, newBaggage, debugId);
}

public JaegerSpanContext withFlags(byte flags) {
return objectFactory.createSpanContext(traceId, spanId, parentId, flags, baggage, debugId);
return objectFactory.createSpanContext(traceIdHigh, traceIdLow, spanId, parentId, flags, baggage, debugId);
}

/**
Expand All @@ -133,7 +157,7 @@ public JaegerSpanContext withFlags(byte flags) {
* @see Constants#BAGGAGE_HEADER_KEY
*/
boolean hasTrace() {
return traceId != 0 && spanId != 0;
return (traceIdLow != 0 || traceIdHigh != 0) && spanId != 0;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public class JaegerTracer implements Tracer, Closeable {
private final Map<String, ?> tags;
private final boolean zipkinSharedRpcSpan;
private final boolean expandExceptionLogs;
private final boolean useTraceId128Bit;

@ToString.Exclude private final PropagationRegistry registry;
@ToString.Exclude private final Clock clock;
Expand All @@ -89,6 +90,7 @@ protected JaegerTracer(JaegerTracer.Builder builder) {
this.baggageSetter = new BaggageSetter(builder.baggageRestrictionManager, metrics);
this.expandExceptionLogs = builder.expandExceptionLogs;
this.objectFactory = builder.objectFactory;
this.useTraceId128Bit = builder.useTraceId128Bit;

this.version = loadVersion();

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

private JaegerSpanContext createNewContext() {
String debugId = getDebugId();
long id = Utils.uniqueId();
long spanId = Utils.uniqueId();
long traceIdLow = spanId;
long traceIdHigh = isUseTraceId128Bit() ? Utils.uniqueId() : 0;

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

return getObjectFactory().createSpanContext(
id,
id,
traceIdHigh,
traceIdLow,
spanId,
0,
flags,
getBaggage(),
Expand Down Expand Up @@ -364,7 +369,8 @@ private JaegerSpanContext createChildContext() {
}

return getObjectFactory().createSpanContext(
preferredReference.getTraceId(),
preferredReference.getTraceIdHigh(),
preferredReference.getTraceIdLow(),
Utils.uniqueId(),
preferredReference.getSpanId(),
// should we do OR across passed references?
Expand Down Expand Up @@ -490,6 +496,7 @@ public static class Builder {
private BaggageRestrictionManager baggageRestrictionManager = new DefaultBaggageRestrictionManager();
private boolean expandExceptionLogs;
private final JaegerObjectFactory objectFactory;
private boolean useTraceId128Bit;

public Builder(String serviceName) {
this(serviceName, new JaegerObjectFactory());
Expand Down Expand Up @@ -577,6 +584,11 @@ public Builder withMetrics(Metrics metrics) {
return this;
}

public Builder withTraceId128Bit() {
this.useTraceId128Bit = true;
return this;
}

public Builder withTag(String key, String value) {
tags.put(key, value);
return this;
Expand Down Expand Up @@ -670,4 +682,8 @@ JaegerSpanContext setBaggage(JaegerSpan jaegerSpan, String key, String value) {
boolean isExpandExceptionLogs() {
return this.expandExceptionLogs;
}

public boolean isUseTraceId128Bit() {
return this.useTraceId128Bit;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2018, The Jaeger Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/

package io.jaegertracing.internal.exceptions;

public class TraceIdOutOfBoundException extends RuntimeException {

private static final long serialVersionUID = 2332452744805504972L;

public TraceIdOutOfBoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ private B3TextMapCodec(Builder builder) {

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

@Override
public JaegerSpanContext extract(TextMap carrier) {
Long traceId = null;
Long traceIdLow = null;
Long traceIdHigh = 0L; // It's enough to check for a null low trace id
Long spanId = null;
Long parentId = 0L; // Conventionally, parent id == 0 means the root span
byte flags = 0;
Expand All @@ -105,7 +107,8 @@ public JaegerSpanContext extract(TextMap carrier) {
flags |= SAMPLED_FLAG;
}
} else if (entry.getKey().equalsIgnoreCase(TRACE_ID_NAME)) {
traceId = HexCodec.lowerHexToUnsignedLong(entry.getValue());
traceIdLow = HexCodec.lowerHexToUnsignedLong(entry.getValue());
traceIdHigh = HexCodec.higherHexToUnsignedLong(entry.getValue());
} else if (entry.getKey().equalsIgnoreCase(PARENT_SPAN_ID_NAME)) {
parentId = HexCodec.lowerHexToUnsignedLong(entry.getValue());
} else if (entry.getKey().equalsIgnoreCase(SPAN_ID_NAME)) {
Expand All @@ -122,9 +125,10 @@ public JaegerSpanContext extract(TextMap carrier) {
}
}

if (null != traceId && null != parentId && null != spanId) {
if (null != traceIdLow && null != parentId && null != spanId) {
JaegerSpanContext spanContext = objectFactory.createSpanContext(
traceId,
traceIdHigh,
traceIdLow,
spanId,
parentId,
flags,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@

package io.jaegertracing.internal.propagation;

import lombok.extern.slf4j.Slf4j;

// copy/pasted from brave.internal.HexCodec 4.1.1 to avoid build complexity
@Slf4j
final class HexCodec {

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

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

return lowerHexToUnsignedLong(lowerHex, beginIndex);
return hexToUnsignedLong(lowerHex, beginIndex, Math.min(beginIndex + 16, lowerHex.length()));
}

/**
* Parses a 1 to 32 character higher-hex string with no prefix into an unsigned long, tossing any
* bits lower than 64.
*
* @return a 64 bit long, meaning that negative values are the overflow of Java's 32 bit long
*/
static Long higherHexToUnsignedLong(String higherHex) {
int length = higherHex.length();
if (length > 32 || length < 1) {
log.debug("token {} size is out of bounds [1, 32]", higherHex);
return null;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be worth a log message somewhere about this...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done at debug Level. I though warn would be also good since it's a situation that should arrive rarely or that can cause tracing to malfunction.
Let me know what you think.

}

return hexToUnsignedLong(higherHex, 0, Math.max(length - 16, 0));
}

/**
Expand All @@ -41,9 +61,9 @@ static Long lowerHexToUnsignedLong(String lowerHex) {
*
* @return a 64 bit long, meaning that negative values are the overflow of Java's 32 bit long
*/
static Long lowerHexToUnsignedLong(String lowerHex, int index) {
static Long hexToUnsignedLong(String lowerHex, int index, int endIndex) {
long result = 0;
for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) {
for (; index < endIndex; index++) {
char c = lowerHex.charAt(index);
result <<= 4;
if (c >= '0' && c <= '9') {
Expand Down
Loading