Merge pull request #392 from DataDog/gary/uint64-id-support

Change the internal storage and handling of trace ID, span ID, and pa…
This commit is contained in:
Gary Huang 2018-07-30 15:01:10 -04:00 committed by GitHub
commit 4c88e1a0a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 446 additions and 132 deletions

View File

@ -47,8 +47,8 @@ class AkkaHttpServerInstrumentationTest extends AgentTestRunner {
assertTraces(TEST_WRITER, 1) {
trace(0, 2) {
span(0) {
traceId 123
parentId 456
traceId "123"
parentId "456"
serviceName "unnamed-java-app"
operationName "akka-http.request"
resourceName "GET /test"

View File

@ -87,7 +87,7 @@ class AWSClientTest extends AgentTestRunner {
span1.resourceName == "apache.http"
span1.type == null
!span1.context().getErrorFlag()
span1.context().parentId == 0
span1.context().parentId == "0"
def tags1 = span1.context().tags
@ -130,7 +130,7 @@ class AWSClientTest extends AgentTestRunner {
span.resourceName == "$service.$operation"
span.type == "web"
!span.context().getErrorFlag()
span.context().parentId == 0
span.context().parentId == "0"
def tags = span.context().tags
tags[Tags.COMPONENT.key] == "java-aws-sdk"

View File

@ -116,7 +116,7 @@ class AWSClientTest extends AgentTestRunner {
span1.resourceName == "apache.http"
span1.type == null
!span1.context().getErrorFlag()
span1.context().parentId == 0
span1.context().parentId == "0"
def tags1 = span1.context().tags
@ -159,7 +159,7 @@ class AWSClientTest extends AgentTestRunner {
span.resourceName == "$service.$operation"
span.type == "web"
!span.context().getErrorFlag()
span.context().parentId == 0
span.context().parentId == "0"
def tags = span.context().tags
tags[Tags.COMPONENT.key] == "java-aws-sdk"

View File

@ -57,7 +57,7 @@ class JaxRsClientTest extends AgentTestRunner {
span.resourceName == "GET /ping"
span.type == "http"
!span.context().getErrorFlag()
span.context().parentId == 0
span.context().parentId == "0"
def tags = span.context().tags

View File

@ -66,7 +66,7 @@ class JettyHandlerTest extends AgentTestRunner {
context.resourceName == "GET ${handler.class.name}"
context.spanType == DDSpanTypes.WEB_SERVLET
!context.getErrorFlag()
context.parentId == 0
context.parentId == "0"
def tags = context.tags
tags["http.url"] == "http://localhost:$port/"
tags["http.method"] == "GET"
@ -154,7 +154,7 @@ class JettyHandlerTest extends AgentTestRunner {
context.resourceName == "GET ${handler.class.name}"
context.spanType == DDSpanTypes.WEB_SERVLET
context.getErrorFlag()
context.parentId == 0
context.parentId == "0"
def tags = context.tags
tags["http.url"] == "http://localhost:$port/"
tags["http.method"] == "GET"

View File

@ -85,7 +85,7 @@ class KafkaClientTest extends AgentTestRunner {
t1span1.resourceName == "Produce Topic $SHARED_TOPIC"
t1span1.type == "queue"
!t1span1.context().getErrorFlag()
t1span1.context().parentId == 0
t1span1.context().parentId == "0"
def t1tags1 = t1span1.context().tags
t1tags1["component"] == "java-kafka"

View File

@ -111,7 +111,7 @@ class KafkaStreamsTest extends AgentTestRunner {
t1span1.resourceName == "Produce Topic $STREAM_PENDING"
t1span1.type == "queue"
!t1span1.context().getErrorFlag()
t1span1.context().parentId == 0
t1span1.context().parentId == "0"
def t1tags1 = t1span1.context().tags
t1tags1["component"] == "java-kafka"

View File

@ -45,7 +45,7 @@ class OkHttp3Test extends AgentTestRunner {
span1.resourceName == "okhttp.http"
span1.type == DDSpanTypes.WEB_SERVLET
!span1.context().getErrorFlag()
span1.context().parentId == 0
span1.context().parentId == "0"
def tags1 = span1.context().tags

View File

@ -45,8 +45,8 @@ class Play26Test extends AgentTestRunner {
assertTraces(TEST_WRITER, 1) {
trace(0, 3) {
span(0) {
traceId 123
parentId 456
traceId "123"
parentId "456"
serviceName "unnamed-java-app"
operationName "akka-http.request"
resourceName "GET /helloplay/:from"

View File

@ -46,8 +46,8 @@ class Play24Test extends AgentTestRunner {
playTrace.size() == 2
playTrace[1].operationName == 'TracedWork$.doWork'
root.traceId == 123
root.parentId == 456
root.traceId == "123"
root.parentId == "456"
root.serviceName == "unnamed-java-app"
root.operationName == "play.request"
root.resourceName == "GET /helloplay/:from"

View File

@ -81,7 +81,7 @@ class SparkJavaBasedTest extends AgentTestRunner {
context.resourceName == "GET /param/:param"
context.spanType == DDSpanTypes.WEB_SERVLET
!context.getErrorFlag()
context.parentId == 0
context.parentId == "0"
def tags = context.tags
tags["http.url"] == "http://localhost:$port/param/asdf1234"
tags["http.method"] == "GET"

View File

@ -43,7 +43,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span.context().resourceName == "GET /param/{parameter}/"
span.context().spanType == DDSpanTypes.WEB_SERVLET
!span.context().getErrorFlag()
span.context().parentId == 0
span.context().parentId == "0"
span.context().tags["http.url"] == "http://localhost:$port/param/asdf1234/"
span.context().tags["http.method"] == "GET"
span.context().tags["span.kind"] == "server"
@ -72,7 +72,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span0.context().resourceName == "404"
span0.context().spanType == DDSpanTypes.WEB_SERVLET
!span0.context().getErrorFlag()
span0.context().parentId == 0
span0.context().parentId == "0"
span0.context().tags["http.url"] == "http://localhost:$port/invalid"
span0.context().tags["http.method"] == "GET"
span0.context().tags["span.kind"] == "server"
@ -92,7 +92,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span1.context().resourceName == "404"
span1.context().spanType == DDSpanTypes.WEB_SERVLET
!span1.context().getErrorFlag()
span1.context().parentId == 0
span1.context().parentId == "0"
span1.context().tags["http.url"] == "http://localhost:$port/error"
span1.context().tags["http.method"] == "GET"
span1.context().tags["span.kind"] == "server"
@ -123,7 +123,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span0.context().resourceName == "GET /error/{parameter}/"
span0.context().spanType == DDSpanTypes.WEB_SERVLET
span0.context().getErrorFlag()
span0.context().parentId == 0
span0.context().parentId == "0"
span0.context().tags["http.url"] == "http://localhost:$port/error/qwerty/"
span0.context().tags["http.method"] == "GET"
span0.context().tags["span.kind"] == "server"
@ -146,7 +146,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span1.context().operationName == "servlet.request"
span1.context().resourceName == "GET /error"
span1.context().spanType == DDSpanTypes.WEB_SERVLET
span1.context().parentId == 0
span1.context().parentId == "0"
span1.context().tags["http.url"] == "http://localhost:$port/error"
span1.context().tags["http.method"] == "GET"
span1.context().tags["span.kind"] == "server"
@ -173,7 +173,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span.context().resourceName == "POST /validated"
span.context().spanType == DDSpanTypes.WEB_SERVLET
!span.context().getErrorFlag()
span.context().parentId == 0
span.context().parentId == "0"
span.context().tags["http.url"] == "http://localhost:$port/validated"
span.context().tags["http.method"] == "POST"
span.context().tags["span.kind"] == "server"
@ -204,7 +204,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span0.context().resourceName == "POST /validated"
span0.context().spanType == DDSpanTypes.WEB_SERVLET
!span0.context().getErrorFlag() // This should be an error once we have the http status code decorator working.
span0.context().parentId == 0
span0.context().parentId == "0"
span0.context().tags["http.url"] == "http://localhost:$port/validated"
span0.context().tags["http.method"] == "POST"
span0.context().tags["span.kind"] == "server"
@ -228,7 +228,7 @@ class SpringBootBasedTest extends AgentTestRunner {
span1.context().resourceName == "POST /error"
span1.context().spanType == DDSpanTypes.WEB_SERVLET
!span1.context().getErrorFlag()
span1.context().parentId == 0
span1.context().parentId == "0"
span1.context().tags["http.url"] == "http://localhost:$port/error"
span1.context().tags["http.method"] == "POST"
span1.context().tags["span.kind"] == "server"

View File

@ -39,14 +39,14 @@ class SpanAssert {
}
def parent() {
assert span.parentId == 0
assert span.parentId == "0"
}
def parentId(long parentId) {
def parentId(String parentId) {
assert span.parentId == parentId
}
def traceId(long traceId) {
def traceId(String traceId) {
assert span.traceId == traceId
}

View File

@ -19,7 +19,7 @@ class TraceCorrelationTest extends AgentTestRunner {
scope.close()
then:
CorrelationIdentifier.traceId == 0
CorrelationIdentifier.spanId == 0
CorrelationIdentifier.traceId == "0"
CorrelationIdentifier.spanId == "0"
}
}

View File

@ -16,29 +16,29 @@ public class CorrelationIdentifier {
}
}
public static long getTraceId() {
public static String getTraceId() {
return provider.get().getTraceId();
}
public static long getSpanId() {
public static String getSpanId() {
return provider.get().getSpanId();
}
public interface Provider {
long getTraceId();
String getTraceId();
long getSpanId();
String getSpanId();
Provider NO_OP =
new Provider() {
@Override
public long getTraceId() {
return 0;
public String getTraceId() {
return "0";
}
@Override
public long getSpanId() {
return 0;
public String getSpanId() {
return "0";
}
};
}

View File

@ -34,7 +34,9 @@ dependencies {
compile deps.jackson
compile deps.slf4j
compile group: 'org.msgpack', name: 'jackson-dataformat-msgpack', version: '0.8.2'
// any higher versions seems to break ES tests with this exception:
// java.lang.NoSuchMethodError: com.fasterxml.jackson.dataformat.smile.SmileGenerator.getOutputContext()
compile group: 'org.msgpack', name: 'jackson-dataformat-msgpack', version: '0.8.14'
testCompile deps.autoservice
testCompile group: 'org.objenesis', name: 'objenesis', version: '2.6'

View File

@ -4,11 +4,16 @@ import static io.opentracing.log.Fields.ERROR_OBJECT;
import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import datadog.trace.api.DDTags;
import datadog.trace.api.interceptor.MutableSpan;
import datadog.trace.api.sampling.PrioritySampling;
import datadog.trace.common.util.Clock;
import io.opentracing.Span;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.WeakReference;
@ -110,7 +115,7 @@ public class DDSpan implements Span, MutableSpan {
*/
@JsonIgnore
public final boolean isRootSpan() {
return context.getParentId() == 0;
return "0".equals(context.getParentId());
}
@Override
@ -317,17 +322,20 @@ public class DDSpan implements Span, MutableSpan {
}
@JsonGetter("trace_id")
public long getTraceId() {
@JsonSerialize(using = UInt64IDStringSerializer.class)
public String getTraceId() {
return context.getTraceId();
}
@JsonGetter("span_id")
public long getSpanId() {
@JsonSerialize(using = UInt64IDStringSerializer.class)
public String getSpanId() {
return context.getSpanId();
}
@JsonGetter("parent_id")
public long getParentId() {
@JsonSerialize(using = UInt64IDStringSerializer.class)
public String getParentId() {
return context.getParentId();
}
@ -390,4 +398,21 @@ public class DDSpan implements Span, MutableSpan {
.append(durationNano)
.toString();
}
protected static class UInt64IDStringSerializer extends StdSerializer<String> {
public UInt64IDStringSerializer() {
this(null);
}
public UInt64IDStringSerializer(Class<String> stringClass) {
super(stringClass);
}
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeNumber(value);
}
}
}

View File

@ -35,9 +35,9 @@ public class DDSpanContext implements io.opentracing.SpanContext {
private final Map<String, String> baggageItems;
// Not Shared with other span contexts
private final long traceId;
private final long spanId;
private final long parentId;
private final String traceId;
private final String spanId;
private final String parentId;
/** Tags are associated to the current span, they will not propagate to the children span */
private final Map<String, Object> tags = new ConcurrentHashMap<>();
@ -67,9 +67,9 @@ public class DDSpanContext implements io.opentracing.SpanContext {
private final long threadId = Thread.currentThread().getId();
public DDSpanContext(
final long traceId,
final long spanId,
final long parentId,
final String traceId,
final String spanId,
final String parentId,
final String serviceName,
final String operationName,
final String resourceName,
@ -86,6 +86,9 @@ public class DDSpanContext implements io.opentracing.SpanContext {
this.tracer = tracer;
this.trace = trace;
assert traceId != null;
assert spanId != null;
assert parentId != null;
this.traceId = traceId;
this.spanId = spanId;
this.parentId = parentId;
@ -111,15 +114,15 @@ public class DDSpanContext implements io.opentracing.SpanContext {
}
}
public long getTraceId() {
public String getTraceId() {
return this.traceId;
}
public long getParentId() {
public String getParentId() {
return this.parentId;
}
public long getSpanId() {
public String getSpanId() {
return this.spanId;
}

View File

@ -465,9 +465,10 @@ public class DDTracer implements io.opentracing.Tracer {
return this;
}
private long generateNewId() {
private String generateNewId() {
// TODO: expand the range of numbers generated to be from 1 to uint 64 MAX
// Ensure the generated ID is in a valid range:
return ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE);
return String.valueOf(ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE));
}
/**
@ -477,9 +478,9 @@ public class DDTracer implements io.opentracing.Tracer {
* @return the context
*/
private DDSpanContext buildSpanContext() {
final long traceId;
final long spanId = generateNewId();
final long parentSpanId;
final String traceId;
final String spanId = generateNewId();
final String parentSpanId;
final Map<String, String> baggage;
final PendingTrace parentTrace;
final int samplingPriority;
@ -521,7 +522,7 @@ public class DDTracer implements io.opentracing.Tracer {
// Start a new trace
} else {
traceId = generateNewId();
parentSpanId = 0L;
parentSpanId = "0";
baggage = null;
parentTrace = new PendingTrace(DDTracer.this, traceId, serviceNameMappings);
samplingPriority = PrioritySampling.UNSET;

View File

@ -22,20 +22,20 @@ public class OTTraceCorrelation implements CorrelationIdentifier.Provider {
}
@Override
public long getTraceId() {
public String getTraceId() {
final Span activeSpan = tracer.activeSpan();
if (activeSpan instanceof DDSpan) {
return ((DDSpan) activeSpan).getTraceId();
}
return 0;
return "0";
}
@Override
public long getSpanId() {
public String getSpanId() {
final Span activeSpan = tracer.activeSpan();
if (activeSpan instanceof DDSpan) {
return ((DDSpan) activeSpan).getSpanId();
}
return 0;
return "0";
}
}

View File

@ -30,7 +30,7 @@ public class PendingTrace extends ConcurrentLinkedDeque<DDSpan> {
}
private final DDTracer tracer;
private final long traceId;
private final String traceId;
private final Map<String, String> serviceNameMappings;
// TODO: consider moving these time fields into DDTracer to ensure that traces have precise
@ -51,7 +51,7 @@ public class PendingTrace extends ConcurrentLinkedDeque<DDSpan> {
private final AtomicBoolean isWritten = new AtomicBoolean(false);
PendingTrace(
final DDTracer tracer, final long traceId, final Map<String, String> serviceNameMappings) {
final DDTracer tracer, final String traceId, final Map<String, String> serviceNameMappings) {
this.tracer = tracer;
this.traceId = traceId;
this.serviceNameMappings = serviceNameMappings;
@ -77,7 +77,13 @@ public class PendingTrace extends ConcurrentLinkedDeque<DDSpan> {
}
public void registerSpan(final DDSpan span) {
if (span.context().getTraceId() != traceId) {
if (traceId == null || span.context() == null) {
log.error(
"Failed to register span ({}) due to null PendingTrace traceId or null span context",
span);
return;
}
if (!traceId.equals(span.context().getTraceId())) {
log.debug("{} - span registered for wrong trace ({})", span, traceId);
return;
}
@ -95,7 +101,12 @@ public class PendingTrace extends ConcurrentLinkedDeque<DDSpan> {
}
private void expireSpan(final DDSpan span) {
if (span.context().getTraceId() != traceId) {
if (traceId == null || span.context() == null) {
log.error(
"Failed to expire span ({}) due to null PendingTrace traceId or null span context", span);
return;
}
if (!traceId.equals(span.context().getTraceId())) {
log.debug("{} - span expired for wrong trace ({})", span, traceId);
return;
}
@ -116,7 +127,12 @@ public class PendingTrace extends ConcurrentLinkedDeque<DDSpan> {
log.debug("{} - added to trace, but not complete.", span);
return;
}
if (traceId != span.getTraceId()) {
if (traceId == null || span.context() == null) {
log.error(
"Failed to add span ({}) due to null PendingTrace traceId or null span context", span);
return;
}
if (!traceId.equals(span.getTraceId())) {
log.debug("{} - added to a mismatched trace.", span);
return;
}

View File

@ -5,16 +5,16 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
public class ExtractedContext implements SpanContext {
private final Long traceId;
private final Long spanId;
private final String traceId;
private final String spanId;
private final int samplingPriority;
private final Map<String, String> baggage;
private final Map<String, String> tags;
private final AtomicBoolean samplingPriorityLocked = new AtomicBoolean(false);
public ExtractedContext(
final Long traceId,
final Long spanId,
final String traceId,
final String spanId,
final int samplingPriority,
final Map<String, String> baggage,
final Map<String, String> tags) {
@ -34,11 +34,11 @@ public class ExtractedContext implements SpanContext {
samplingPriorityLocked.set(true);
}
public Long getTraceId() {
public String getTraceId() {
return traceId;
}
public Long getSpanId() {
public String getSpanId() {
return spanId;
}

View File

@ -4,6 +4,7 @@ import datadog.opentracing.DDSpanContext;
import datadog.trace.api.sampling.PrioritySampling;
import io.opentracing.propagation.TextMap;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collections;
@ -15,6 +16,10 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class HTTPCodec implements Codec<TextMap> {
// uint 64 bits max value, 2^64 - 1
static final BigInteger BIG_INTEGER_UINT64_MAX =
(new BigInteger("2")).pow(64).subtract(BigInteger.ONE);
private static final String OT_BAGGAGE_PREFIX = "ot-baggage-";
private static final String TRACE_ID_KEY = "x-datadog-trace-id";
private static final String SPAN_ID_KEY = "x-datadog-parent-id";
@ -48,34 +53,36 @@ public class HTTPCodec implements Codec<TextMap> {
Map<String, String> baggage = Collections.emptyMap();
Map<String, String> tags = Collections.emptyMap();
Long traceId = 0L;
Long spanId = 0L;
String traceId = "0";
String spanId = "0";
int samplingPriority = PrioritySampling.UNSET;
for (final Map.Entry<String, String> entry : carrier) {
final String key = entry.getKey().toLowerCase();
if (key.equalsIgnoreCase(TRACE_ID_KEY)) {
traceId = Long.parseLong(entry.getValue());
} else if (key.equalsIgnoreCase(SPAN_ID_KEY)) {
spanId = Long.parseLong(entry.getValue());
final String val = entry.getValue();
if (TRACE_ID_KEY.equalsIgnoreCase(key)) {
traceId = validateUInt64BitsID(val);
} else if (SPAN_ID_KEY.equalsIgnoreCase(key)) {
spanId = validateUInt64BitsID(val);
} else if (key.startsWith(OT_BAGGAGE_PREFIX)) {
if (baggage.isEmpty()) {
baggage = new HashMap<>();
}
baggage.put(key.replace(OT_BAGGAGE_PREFIX, ""), decode(entry.getValue()));
} else if (key.equalsIgnoreCase(SAMPLING_PRIORITY_KEY)) {
samplingPriority = Integer.parseInt(entry.getValue());
baggage.put(key.replace(OT_BAGGAGE_PREFIX, ""), decode(val));
} else if (SAMPLING_PRIORITY_KEY.equalsIgnoreCase(key)) {
samplingPriority = Integer.parseInt(val);
}
if (taggedHeaders.containsKey(key)) {
if (tags.isEmpty()) {
tags = new HashMap<>();
}
tags.put(taggedHeaders.get(key), decode(entry.getValue()));
tags.put(taggedHeaders.get(key), decode(val));
}
}
ExtractedContext context = null;
if (traceId != 0L) {
if (!"0".equals(traceId)) {
context = new ExtractedContext(traceId, spanId, samplingPriority, baggage, tags);
context.lockSamplingPriority();
@ -104,4 +111,27 @@ public class HTTPCodec implements Codec<TextMap> {
}
return decoded;
}
/**
* Helper method to validate an ID String to verify that it is an unsigned 64 bits number and is
* within range.
*
* @param val the String that contains the ID
* @return the ID in String format if it passes validations
* @throws IllegalArgumentException if val is not a number or if the number is out of range
*/
private String validateUInt64BitsID(String val) throws IllegalArgumentException {
try {
BigInteger validate = new BigInteger(val);
if (validate.compareTo(BigInteger.ZERO) == -1
|| validate.compareTo(BIG_INTEGER_UINT64_MAX) == 1) {
throw new IllegalArgumentException(
"ID out of range, must be between 0 and 2^64-1, got: " + val);
}
return val;
} catch (NumberFormatException nfe) {
throw new IllegalArgumentException(
"Expecting a number for trace ID or span ID, but got: " + val, nfe);
}
}
}

View File

@ -118,7 +118,7 @@ public class DDApi {
return false;
}
log.debug("Succesfully sent {} of {} traces to the DD agent.", traces.size(), totalSize);
log.debug("Successfully sent {} of {} traces to the DD agent.", traces.size(), totalSize);
try {
if (null != responseString
@ -130,7 +130,7 @@ public class DDApi {
}
}
} catch (final IOException e) {
log.debug("failed to parse DD agent response: " + responseString, e);
log.debug("Failed to parse DD agent response: " + responseString, e);
}
return true;

View File

@ -141,14 +141,14 @@ class DDSpanBuilderTest extends Specification {
def "should link to parent span"() {
setup:
final long spanId = 1L
final String spanId = "1"
final long expectedParentId = spanId
final DDSpanContext mockedContext = mock(DDSpanContext)
when(mockedContext.getTraceId()).thenReturn(spanId)
when(mockedContext.getSpanId()).thenReturn(spanId)
when(mockedContext.getServiceName()).thenReturn("foo")
when(mockedContext.getTrace()).thenReturn(new PendingTrace(tracer, 1L, [:]))
when(mockedContext.getTrace()).thenReturn(new PendingTrace(tracer, "1", [:]))
final String expectedName = "fakeName"
@ -163,6 +163,7 @@ class DDSpanBuilderTest extends Specification {
expect:
actualContext.getParentId() == expectedParentId
actualContext.getTraceId() == spanId
}
def "should inherit the DD parent attributes"() {
@ -265,8 +266,8 @@ class DDSpanBuilderTest extends Specification {
where:
extractedContext | _
new ExtractedContext(1, 2, 0, [:], [:]) | _
new ExtractedContext(3, 4, 1, ["asdf": "qwer"], ["zxcv": "1234"]) | _
new ExtractedContext("1", "2", 0, [:], [:]) | _
new ExtractedContext("3", "4", 1, ["asdf": "qwer"], ["zxcv": "1234"]) | _
}
def "global span tags populated on each span"() {

View File

@ -39,9 +39,9 @@ class DDSpanSerializationTest extends Specification {
def tracer = new DDTracer(writer)
final DDSpanContext context =
new DDSpanContext(
1L,
2L,
0L,
"1",
"2",
"0",
"service",
"operation",
null,
@ -50,7 +50,7 @@ class DDSpanSerializationTest extends Specification {
false,
"type",
tags,
new PendingTrace(tracer, 1L, [:]),
new PendingTrace(tracer, "1", [:]),
tracer)
baggage.put(DDTags.THREAD_NAME, Thread.currentThread().getName())

View File

@ -15,9 +15,9 @@ class DDSpanTest extends Specification {
setup:
final DDSpanContext context =
new DDSpanContext(
1L,
1L,
0L,
"1",
"1",
"0",
"fakeService",
"fakeOperation",
"fakeResource",
@ -26,7 +26,7 @@ class DDSpanTest extends Specification {
false,
"fakeType",
null,
new PendingTrace(tracer, 1L, [:]),
new PendingTrace(tracer, "1", [:]),
tracer)
final DDSpan span = new DDSpan(1L, context)

View File

@ -24,7 +24,7 @@ class OTTraceCorrelationTest extends Specification {
scope.close()
expect:
0 == traceCorrelation.getTraceId()
"0" == traceCorrelation.getTraceId()
}
def "get trace id with trace"() {
@ -37,7 +37,7 @@ class OTTraceCorrelationTest extends Specification {
scope.close()
expect:
0 == traceCorrelation.getSpanId()
"0" == traceCorrelation.getSpanId()
}
def "get span id with trace"() {

View File

@ -13,9 +13,10 @@ class PendingTraceTest extends Specification {
def traceCount = tracer.traceCount
def traceId = System.identityHashCode(this)
String traceIdStr = String.valueOf(traceId)
@Subject
PendingTrace trace = new PendingTrace(tracer, traceId, [:])
PendingTrace trace = new PendingTrace(tracer, traceIdStr, [:])
DDSpan rootSpan = SpanFactory.newSpanOf(trace)
@ -130,7 +131,7 @@ class PendingTraceTest extends Specification {
def "register span to wrong trace fails"() {
setup:
def otherTrace = new PendingTrace(tracer, traceId - 10, [:])
def otherTrace = new PendingTrace(tracer, String.valueOf(traceId - 10), [:])
otherTrace.registerSpan(new DDSpan(0, rootSpan.context()))
expect:
@ -141,7 +142,7 @@ class PendingTraceTest extends Specification {
def "add span to wrong trace fails"() {
setup:
def otherTrace = new PendingTrace(tracer, traceId - 10, [:])
def otherTrace = new PendingTrace(tracer, String.valueOf(traceId - 10), [:])
rootSpan.finish()
otherTrace.addSpan(rootSpan)

View File

@ -8,9 +8,9 @@ class SpanFactory {
def writer = new ListWriter()
def tracer = new DDTracer(writer)
def context = new DDSpanContext(
1L,
1L,
0L,
"1",
"1",
"0",
"fakeService",
"fakeOperation",
"fakeResource",
@ -19,16 +19,16 @@ class SpanFactory {
false,
"fakeType",
Collections.emptyMap(),
new PendingTrace(tracer, 1L, [:]),
new PendingTrace(tracer, "1", [:]),
tracer)
return new DDSpan(timestampMicro, context)
}
static newSpanOf(DDTracer tracer) {
def context = new DDSpanContext(
1L,
1L,
0L,
"1",
"1",
"0",
"fakeService",
"fakeOperation",
"fakeResource",
@ -37,7 +37,7 @@ class SpanFactory {
false,
"fakeType",
Collections.emptyMap(),
new PendingTrace(tracer, 1L, [:]),
new PendingTrace(tracer, "1", [:]),
tracer)
return new DDSpan(1, context)
}
@ -45,8 +45,8 @@ class SpanFactory {
static newSpanOf(PendingTrace trace) {
def context = new DDSpanContext(
trace.traceId,
1L,
0L,
"1",
"0",
"fakeService",
"fakeOperation",
"fakeResource",
@ -64,9 +64,9 @@ class SpanFactory {
def writer = new ListWriter()
def tracer = new DDTracer(writer)
def context = new DDSpanContext(
1L,
1L,
0L,
"1",
"1",
"0",
serviceName,
"fakeOperation",
"fakeResource",
@ -75,7 +75,7 @@ class SpanFactory {
false,
"fakeType",
Collections.emptyMap(),
new PendingTrace(tracer, 1L, [:]),
new PendingTrace(tracer, "1", [:]),
tracer)
context.setTag("env", envName)
return new DDSpan(0l, context)

View File

@ -87,9 +87,9 @@ class URLAsResourceNameTest extends Specification {
when:
final DDSpanContext context =
new DDSpanContext(
1L,
1L,
0L,
"1",
"1",
"0",
"fakeService",
"fakeOperation",
"fakeResource",
@ -98,7 +98,7 @@ class URLAsResourceNameTest extends Specification {
false,
"fakeType",
tags,
new PendingTrace(tracer, 1L, [:]),
new PendingTrace(tracer, "1", [:]),
tracer)
then:

View File

@ -10,6 +10,8 @@ import io.opentracing.propagation.TextMapInjectAdapter
import spock.lang.Shared
import spock.lang.Specification
import static datadog.opentracing.propagation.HTTPCodec.BIG_INTEGER_UINT64_MAX
class HTTPCodecTest extends Specification {
@Shared
private static final String OT_BAGGAGE_PREFIX = "ot-baggage-"
@ -28,9 +30,9 @@ class HTTPCodecTest extends Specification {
def tracer = new DDTracer(writer)
final DDSpanContext mockedContext =
new DDSpanContext(
1L,
2L,
0L,
"1",
"2",
"0",
"fakeService",
"fakeOperation",
"fakeResource",
@ -44,7 +46,7 @@ class HTTPCodecTest extends Specification {
false,
"fakeType",
null,
new PendingTrace(tracer, 1L, [:]),
new PendingTrace(tracer, "1", [:]),
tracer)
final Map<String, String> carrier = new HashMap<>()
@ -64,6 +66,96 @@ class HTTPCodecTest extends Specification {
PrioritySampling.SAMPLER_KEEP | _
}
def "inject http headers with larger than Java long IDs"() {
String largeTraceId = "9523372036854775807"
String largeSpanId = "15815582334751494918"
String largeParentId = "15815582334751494914"
setup:
def writer = new ListWriter()
def tracer = new DDTracer(writer)
final DDSpanContext mockedContext =
new DDSpanContext(
largeTraceId,
largeSpanId,
largeParentId,
"fakeService",
"fakeOperation",
"fakeResource",
samplingPriority,
new HashMap<String, String>() {
{
put("k1", "v1")
put("k2", "v2")
}
},
false,
"fakeType",
null,
new PendingTrace(tracer, largeTraceId, [:]),
tracer)
final Map<String, String> carrier = new HashMap<>()
codec.inject(mockedContext, new TextMapInjectAdapter(carrier))
expect:
carrier.get(TRACE_ID_KEY) == largeTraceId
carrier.get(SPAN_ID_KEY) == largeSpanId
carrier.get(SAMPLING_PRIORITY_KEY) == (samplingPriority == PrioritySampling.UNSET ? null : String.valueOf(samplingPriority))
carrier.get(OT_BAGGAGE_PREFIX + "k1") == "v1"
carrier.get(OT_BAGGAGE_PREFIX + "k2") == "v2"
where:
samplingPriority | _
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
def "inject http headers with uint 64 max IDs"() {
String largeTraceId = "18446744073709551615"
String largeSpanId = "18446744073709551614"
String largeParentId = "18446744073709551613"
setup:
def writer = new ListWriter()
def tracer = new DDTracer(writer)
final DDSpanContext mockedContext =
new DDSpanContext(
largeTraceId,
largeSpanId,
largeParentId,
"fakeService",
"fakeOperation",
"fakeResource",
samplingPriority,
new HashMap<String, String>() {
{
put("k1", "v1")
put("k2", "v2")
}
},
false,
"fakeType",
null,
new PendingTrace(tracer, largeTraceId, [:]),
tracer)
final Map<String, String> carrier = new HashMap<>()
codec.inject(mockedContext, new TextMapInjectAdapter(carrier))
expect:
carrier.get(TRACE_ID_KEY) == largeTraceId
carrier.get(SPAN_ID_KEY) == largeSpanId
carrier.get(SAMPLING_PRIORITY_KEY) == (samplingPriority == PrioritySampling.UNSET ? null : String.valueOf(samplingPriority))
carrier.get(OT_BAGGAGE_PREFIX + "k1") == "v1"
carrier.get(OT_BAGGAGE_PREFIX + "k2") == "v2"
where:
samplingPriority | _
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
def "extract http headers"() {
setup:
final Map<String, String> actual = [
@ -81,8 +173,8 @@ class HTTPCodecTest extends Specification {
final ExtractedContext context = codec.extract(new TextMapExtractAdapter(actual))
expect:
context.getTraceId() == 1l
context.getSpanId() == 2l
context.getTraceId() == "1"
context.getSpanId() == "2"
context.getBaggage().get("k1") == "v1"
context.getBaggage().get("k2") == "v2"
context.getTags() == ["some-tag": "my-interesting-info"]
@ -93,4 +185,147 @@ class HTTPCodecTest extends Specification {
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
def "extract http headers with larger than Java long IDs"() {
setup:
String largeTraceId = "9523372036854775807"
String largeSpanId = "15815582334751494918"
final Map<String, String> actual = [
(TRACE_ID_KEY.toUpperCase()) : largeTraceId,
(SPAN_ID_KEY.toUpperCase()) : largeSpanId,
(OT_BAGGAGE_PREFIX.toUpperCase() + "k1"): "v1",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k2"): "v2",
SOME_HEADER : "my-interesting-info",
]
if (samplingPriority != PrioritySampling.UNSET) {
actual.put(SAMPLING_PRIORITY_KEY, String.valueOf(samplingPriority))
}
final ExtractedContext context = codec.extract(new TextMapExtractAdapter(actual))
expect:
context.getTraceId() == largeTraceId
context.getSpanId() == largeSpanId
context.getBaggage().get("k1") == "v1"
context.getBaggage().get("k2") == "v2"
context.getTags() == ["some-tag": "my-interesting-info"]
context.getSamplingPriority() == samplingPriority
where:
samplingPriority | _
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
def "extract http headers with uint 64 max IDs"() {
setup:
String largeSpanId = BIG_INTEGER_UINT64_MAX.subtract(BigInteger.ONE).toString()
final Map<String, String> actual = [
(TRACE_ID_KEY.toUpperCase()) : BIG_INTEGER_UINT64_MAX.toString(),
(SPAN_ID_KEY.toUpperCase()) : BIG_INTEGER_UINT64_MAX.minus(1).toString(),
(OT_BAGGAGE_PREFIX.toUpperCase() + "k1"): "v1",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k2"): "v2",
SOME_HEADER : "my-interesting-info",
]
if (samplingPriority != PrioritySampling.UNSET) {
actual.put(SAMPLING_PRIORITY_KEY, String.valueOf(samplingPriority))
}
final ExtractedContext context = codec.extract(new TextMapExtractAdapter(actual))
expect:
context.getTraceId() == BIG_INTEGER_UINT64_MAX.toString()
context.getSpanId() == largeSpanId
context.getBaggage().get("k1") == "v1"
context.getBaggage().get("k2") == "v2"
context.getTags() == ["some-tag": "my-interesting-info"]
context.getSamplingPriority() == samplingPriority
where:
samplingPriority | _
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
def "extract http headers with invalid non-numeric ID"() {
setup:
final Map<String, String> actual = [
(TRACE_ID_KEY.toUpperCase()) : "traceID",
(SPAN_ID_KEY.toUpperCase()) : "spanID",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k1"): "v1",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k2"): "v2",
SOME_HEADER : "my-interesting-info",
]
if (samplingPriority != PrioritySampling.UNSET) {
actual.put(SAMPLING_PRIORITY_KEY, String.valueOf(samplingPriority))
}
when:
codec.extract(new TextMapExtractAdapter(actual))
then:
def iae = thrown(IllegalArgumentException)
assert iae.cause instanceof NumberFormatException
where:
samplingPriority | _
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
def "extract http headers with out of range trace ID"() {
setup:
String outOfRangeTraceId = BIG_INTEGER_UINT64_MAX.add(BigInteger.ONE).toString()
final Map<String, String> actual = [
(TRACE_ID_KEY.toUpperCase()) : outOfRangeTraceId,
(SPAN_ID_KEY.toUpperCase()) : "0",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k1"): "v1",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k2"): "v2",
SOME_HEADER : "my-interesting-info",
]
if (samplingPriority != PrioritySampling.UNSET) {
actual.put(SAMPLING_PRIORITY_KEY, String.valueOf(samplingPriority))
}
when:
codec.extract(new TextMapExtractAdapter(actual))
then:
thrown(IllegalArgumentException)
where:
samplingPriority | _
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
def "extract http headers with out of range span ID"() {
setup:
final Map<String, String> actual = [
(TRACE_ID_KEY.toUpperCase()) : "0",
(SPAN_ID_KEY.toUpperCase()) : "-1",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k1"): "v1",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k2"): "v2",
SOME_HEADER : "my-interesting-info",
]
if (samplingPriority != PrioritySampling.UNSET) {
actual.put(SAMPLING_PRIORITY_KEY, String.valueOf(samplingPriority))
}
when:
codec.extract(new TextMapExtractAdapter(actual))
then:
thrown(IllegalArgumentException)
where:
samplingPriority | _
PrioritySampling.UNSET | _
PrioritySampling.SAMPLER_KEEP | _
}
}

View File

@ -17,9 +17,9 @@ class DDApiIntegrationTest {
static final WRITER = new ListWriter()
static final TRACER = new DDTracer(WRITER)
static final CONTEXT = new DDSpanContext(
1L,
1L,
0L,
"1",
"1",
"0",
"fakeService",
"fakeOperation",
"fakeResource",
@ -28,7 +28,7 @@ class DDApiIntegrationTest {
false,
"fakeType",
Collections.emptyMap(),
new PendingTrace(TRACER, 1L, [:]),
new PendingTrace(TRACER, "1", [:]),
TRACER)
def api = new DDApi(DDAgentWriter.DEFAULT_HOSTNAME, DDAgentWriter.DEFAULT_PORT, v4())