Configure tags to be added to every span
This commit is contained in:
parent
b3c8c3fd6b
commit
759fb4e815
|
@ -148,7 +148,7 @@ public class DDSpanContext implements io.opentracing.SpanContext {
|
|||
this.spanType = spanType;
|
||||
}
|
||||
|
||||
public void setSamplingPriority(int newPriority) {
|
||||
public void setSamplingPriority(final int newPriority) {
|
||||
if (samplingPriorityLocked) {
|
||||
log.warn(
|
||||
"samplingPriority locked at {}. Refusing to set to {}", samplingPriority, newPriority);
|
||||
|
@ -272,7 +272,7 @@ public class DDSpanContext implements io.opentracing.SpanContext {
|
|||
|
||||
public synchronized Map<String, Object> getTags() {
|
||||
if (tags.isEmpty()) {
|
||||
tags = Maps.newHashMapWithExpectedSize(2);
|
||||
tags = Maps.newHashMapWithExpectedSize(3);
|
||||
}
|
||||
tags.put(DDTags.THREAD_NAME, threadName);
|
||||
tags.put(DDTags.THREAD_ID, threadId);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package datadog.opentracing;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.google.common.collect.Maps;
|
||||
import datadog.opentracing.decorators.AbstractDecorator;
|
||||
import datadog.opentracing.decorators.DDDecoratorsFactory;
|
||||
import datadog.opentracing.propagation.Codec;
|
||||
|
@ -44,6 +45,9 @@ public class DDTracer extends ThreadLocalScopeManager implements io.opentracing.
|
|||
/** Sampler defines the sampling policy in order to reduce the number of traces for instance */
|
||||
final Sampler sampler;
|
||||
|
||||
/** A set of tags that are added to every span */
|
||||
private final Map<String, Object> spanTags;
|
||||
|
||||
/** Span context decorators */
|
||||
private final Map<String, List<AbstractDecorator>> spanContextDecorators = new HashMap<>();
|
||||
|
||||
|
@ -63,7 +67,8 @@ public class DDTracer extends ThreadLocalScopeManager implements io.opentracing.
|
|||
this(
|
||||
config.getProperty(DDTraceConfig.SERVICE_NAME),
|
||||
Writer.Builder.forConfig(config),
|
||||
Sampler.Builder.forConfig(config));
|
||||
Sampler.Builder.forConfig(config),
|
||||
DDTraceConfig.parseMap(config.getProperty(DDTraceConfig.SPAN_TAGS)));
|
||||
log.debug("Using config: {}", config);
|
||||
|
||||
// Create decorators from resource files
|
||||
|
@ -75,10 +80,20 @@ public class DDTracer extends ThreadLocalScopeManager implements io.opentracing.
|
|||
}
|
||||
|
||||
public DDTracer(final String serviceName, final Writer writer, final Sampler sampler) {
|
||||
this(serviceName, writer, sampler, Collections.<String, Object>emptyMap());
|
||||
}
|
||||
|
||||
public DDTracer(
|
||||
final String serviceName,
|
||||
final Writer writer,
|
||||
final Sampler sampler,
|
||||
final Map<String, Object> spanTags) {
|
||||
this.serviceName = serviceName;
|
||||
this.writer = writer;
|
||||
this.writer.start();
|
||||
this.sampler = sampler;
|
||||
this.spanTags = spanTags;
|
||||
|
||||
registry = new CodecRegistry();
|
||||
registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec());
|
||||
registry.register(Format.Builtin.TEXT_MAP, new HTTPCodec());
|
||||
|
@ -90,7 +105,11 @@ public class DDTracer extends ThreadLocalScopeManager implements io.opentracing.
|
|||
}
|
||||
|
||||
public DDTracer(final Writer writer) {
|
||||
this(UNASSIGNED_DEFAULT_SERVICE_NAME, writer, new AllSampler());
|
||||
this(
|
||||
UNASSIGNED_DEFAULT_SERVICE_NAME,
|
||||
writer,
|
||||
new AllSampler(),
|
||||
DDTraceConfig.parseMap(new DDTraceConfig().getProperty(DDTraceConfig.SPAN_TAGS)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -185,6 +204,8 @@ public class DDTracer extends ThreadLocalScopeManager implements io.opentracing.
|
|||
+ writer
|
||||
+ ", sampler="
|
||||
+ sampler
|
||||
+ ", tags="
|
||||
+ spanTags
|
||||
+ '}';
|
||||
}
|
||||
|
||||
|
@ -236,7 +257,8 @@ public class DDTracer extends ThreadLocalScopeManager implements io.opentracing.
|
|||
private final String operationName;
|
||||
|
||||
// Builder attributes
|
||||
private Map<String, Object> tags = Collections.emptyMap();
|
||||
private Map<String, Object> tags =
|
||||
spanTags.isEmpty() ? Collections.<String, Object>emptyMap() : Maps.newHashMap(spanTags);
|
||||
private long timestamp;
|
||||
private SpanContext parent;
|
||||
private String serviceName;
|
||||
|
@ -361,7 +383,7 @@ public class DDTracer extends ThreadLocalScopeManager implements io.opentracing.
|
|||
// Private methods
|
||||
private DDSpanBuilder withTag(final String tag, final Object value) {
|
||||
if (this.tags.isEmpty()) {
|
||||
this.tags = new HashMap<>();
|
||||
this.tags = Maps.newHashMap();
|
||||
}
|
||||
this.tags.put(tag, value);
|
||||
return this;
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package datadog.trace.common;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import datadog.opentracing.DDTracer;
|
||||
import datadog.trace.common.writer.DDAgentWriter;
|
||||
import datadog.trace.common.writer.Writer;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Config gives priority to system properties and falls back to environment variables. It also
|
||||
|
@ -14,6 +18,7 @@ import java.util.Properties;
|
|||
* <p>System properties are {@link DDTraceConfig#PREFIX}'ed. Environment variables are the same as
|
||||
* the system property, but uppercased with '.' -> '_'.
|
||||
*/
|
||||
@Slf4j
|
||||
public class DDTraceConfig extends Properties {
|
||||
/** Config keys below */
|
||||
private static final String PREFIX = "dd.";
|
||||
|
@ -23,12 +28,14 @@ public class DDTraceConfig extends Properties {
|
|||
public static final String AGENT_HOST = "agent.host";
|
||||
public static final String AGENT_PORT = "agent.port";
|
||||
public static final String PRIORITY_SAMPLING = "priority.sampling";
|
||||
public static final String SPAN_TAGS = "trace.span.tags";
|
||||
|
||||
private final String serviceName = getPropOrEnv(PREFIX + SERVICE_NAME);
|
||||
private final String writerType = getPropOrEnv(PREFIX + WRITER_TYPE);
|
||||
private final String agentHost = getPropOrEnv(PREFIX + AGENT_HOST);
|
||||
private final String agentPort = getPropOrEnv(PREFIX + AGENT_PORT);
|
||||
private final String prioritySampling = getPropOrEnv(PREFIX + PRIORITY_SAMPLING);
|
||||
private final String spanTags = getPropOrEnv(PREFIX + SPAN_TAGS);
|
||||
|
||||
public DDTraceConfig() {
|
||||
super();
|
||||
|
@ -45,6 +52,7 @@ public class DDTraceConfig extends Properties {
|
|||
setIfNotNull(AGENT_HOST, agentHost);
|
||||
setIfNotNull(AGENT_PORT, agentPort);
|
||||
setIfNotNull(PRIORITY_SAMPLING, prioritySampling);
|
||||
setIfNotNull(SPAN_TAGS, spanTags);
|
||||
}
|
||||
|
||||
public DDTraceConfig(final String serviceName) {
|
||||
|
@ -65,4 +73,23 @@ public class DDTraceConfig extends Properties {
|
|||
static String propToEnvName(final String name) {
|
||||
return name.toUpperCase().replace(".", "_");
|
||||
}
|
||||
|
||||
public static Map<String, Object> parseMap(final String str) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
if (!str.matches("(([^,:]+:[^,:]+,)*([^,:]+:[^,:]+),?)?")) {
|
||||
log.warn("Invalid config '{}'. Must match 'key1:value1,key2:value2'.", str);
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
final String[] tokens = str.split(",");
|
||||
final Map<String, Object> map = Maps.newHashMapWithExpectedSize(tokens.length);
|
||||
|
||||
for (final String token : tokens) {
|
||||
final String[] keyValue = token.split(":");
|
||||
map.put(keyValue[0].trim(), keyValue[1].trim());
|
||||
}
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,241 @@
|
|||
package datadog.opentracing
|
||||
|
||||
import datadog.trace.api.DDTags
|
||||
import datadog.trace.common.writer.ListWriter
|
||||
import spock.lang.Specification
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
import static org.mockito.Mockito.mock
|
||||
import static org.mockito.Mockito.when
|
||||
|
||||
class DDSpanBuilderTest extends Specification {
|
||||
def writer = new ListWriter()
|
||||
def tracer = new DDTracer(writer)
|
||||
|
||||
def "build simple span"() {
|
||||
setup:
|
||||
final DDSpan span = tracer.buildSpan("op name").withServiceName("foo").start()
|
||||
|
||||
expect:
|
||||
span.operationName == "op name"
|
||||
}
|
||||
|
||||
def "build complex span"() {
|
||||
setup:
|
||||
def expectedName = "fakeName"
|
||||
def tags = [
|
||||
"1": true,
|
||||
"2": "fakeString",
|
||||
"3": 42.0,
|
||||
]
|
||||
|
||||
DDTracer.DDSpanBuilder builder = tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
tags.each {
|
||||
builder = builder.withTag(it.key, it.value)
|
||||
}
|
||||
|
||||
when:
|
||||
DDSpan span = builder.start()
|
||||
|
||||
then:
|
||||
span.getOperationName() == expectedName
|
||||
span.tags.subMap(tags.keySet()) == tags
|
||||
|
||||
|
||||
when:
|
||||
span = tracer.buildSpan(expectedName).withServiceName("foo").start()
|
||||
|
||||
then:
|
||||
span.getTags() == [(DDTags.THREAD_NAME): Thread.currentThread().getName(),
|
||||
(DDTags.THREAD_ID) : Thread.currentThread().getId(),
|
||||
(DDTags.SPAN_TYPE) : null]
|
||||
|
||||
when:
|
||||
// with all custom fields provided
|
||||
final String expectedResource = "fakeResource"
|
||||
final String expectedService = "fakeService"
|
||||
final String expectedType = "fakeType"
|
||||
|
||||
span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.withResourceName(expectedResource)
|
||||
.withServiceName(expectedService)
|
||||
.withErrorFlag()
|
||||
.withSpanType(expectedType)
|
||||
.start()
|
||||
|
||||
final DDSpanContext context = span.context()
|
||||
|
||||
then:
|
||||
context.getResourceName() == expectedResource
|
||||
context.getErrorFlag()
|
||||
context.getServiceName() == expectedService
|
||||
context.getSpanType() == expectedType
|
||||
|
||||
context.tags[DDTags.THREAD_NAME] == Thread.currentThread().getName()
|
||||
context.tags[DDTags.THREAD_ID] == Thread.currentThread().getId()
|
||||
}
|
||||
|
||||
def "should build span timestamp in nano"() {
|
||||
setup:
|
||||
// time in micro
|
||||
final long expectedTimestamp = 487517802L * 1000 * 1000L
|
||||
final String expectedName = "fakeName"
|
||||
|
||||
DDSpan span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.withStartTimestamp(expectedTimestamp)
|
||||
.start()
|
||||
|
||||
expect:
|
||||
// get return nano time
|
||||
span.getStartTime() == expectedTimestamp * 1000L
|
||||
|
||||
when:
|
||||
// auto-timestamp in nanoseconds
|
||||
def start = System.currentTimeMillis()
|
||||
span = tracer.buildSpan(expectedName).withServiceName("foo").start()
|
||||
def stop = System.currentTimeMillis()
|
||||
|
||||
then:
|
||||
// Give a range of +/- 5 millis
|
||||
span.getStartTime() >= MILLISECONDS.toNanos(start - 1)
|
||||
span.getStartTime() <= MILLISECONDS.toNanos(stop + 1)
|
||||
}
|
||||
|
||||
def "should link to parent span"() {
|
||||
setup:
|
||||
final long spanId = 1L
|
||||
final long expectedParentId = spanId
|
||||
|
||||
final DDSpanContext mockedContext = mock(DDSpanContext)
|
||||
|
||||
when(mockedContext.getSpanId()).thenReturn(spanId)
|
||||
when(mockedContext.getServiceName()).thenReturn("foo")
|
||||
|
||||
final String expectedName = "fakeName"
|
||||
|
||||
final DDSpan span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.asChildOf(mockedContext)
|
||||
.start()
|
||||
|
||||
final DDSpanContext actualContext = span.context()
|
||||
|
||||
expect:
|
||||
actualContext.getParentId() == expectedParentId
|
||||
}
|
||||
|
||||
def "should inherit the DD parent attributes"() {
|
||||
setup:
|
||||
def expectedName = "fakeName"
|
||||
def expectedParentServiceName = "fakeServiceName"
|
||||
def expectedParentResourceName = "fakeResourceName"
|
||||
def expectedParentType = "fakeType"
|
||||
def expectedChildServiceName = "fakeServiceName-child"
|
||||
def expectedChildResourceName = "fakeResourceName-child"
|
||||
def expectedChildType = "fakeType-child"
|
||||
def expectedBaggageItemKey = "fakeKey"
|
||||
def expectedBaggageItemValue = "fakeValue"
|
||||
|
||||
final DDSpan parent =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.withResourceName(expectedParentResourceName)
|
||||
.withSpanType(expectedParentType)
|
||||
.start()
|
||||
|
||||
parent.setBaggageItem(expectedBaggageItemKey, expectedBaggageItemValue)
|
||||
|
||||
// ServiceName and SpanType are always set by the parent if they are not present in the child
|
||||
DDSpan span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName(expectedParentServiceName)
|
||||
.asChildOf(parent)
|
||||
.start()
|
||||
|
||||
expect:
|
||||
span.getOperationName() == expectedName
|
||||
span.getBaggageItem(expectedBaggageItemKey) == expectedBaggageItemValue
|
||||
span.context().getServiceName() == expectedParentServiceName
|
||||
span.context().getResourceName() == expectedName
|
||||
span.context().getSpanType() == expectedParentType
|
||||
|
||||
when:
|
||||
// ServiceName and SpanType are always overwritten by the child if they are present
|
||||
span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName(expectedChildServiceName)
|
||||
.withResourceName(expectedChildResourceName)
|
||||
.withSpanType(expectedChildType)
|
||||
.asChildOf(parent)
|
||||
.start()
|
||||
|
||||
then:
|
||||
span.getOperationName() == expectedName
|
||||
span.getBaggageItem(expectedBaggageItemKey) == expectedBaggageItemValue
|
||||
span.context().getServiceName() == expectedChildServiceName
|
||||
span.context().getResourceName() == expectedChildResourceName
|
||||
span.context().getSpanType() == expectedChildType
|
||||
}
|
||||
|
||||
def "should track all spans in trace"() {
|
||||
setup:
|
||||
List<DDSpan> spans = []
|
||||
final int nbSamples = 10
|
||||
|
||||
// root (aka spans[0]) is the parent
|
||||
// others are just for fun
|
||||
|
||||
def root = tracer.buildSpan("fake_O").withServiceName("foo").start()
|
||||
spans.add(root)
|
||||
|
||||
final long tickEnd = System.currentTimeMillis()
|
||||
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
spans.add(tracer
|
||||
.buildSpan("fake_" + i)
|
||||
.withServiceName("foo")
|
||||
.asChildOf(spans.get(i - 1))
|
||||
.start())
|
||||
}
|
||||
spans.get(1).finish(tickEnd)
|
||||
|
||||
expect:
|
||||
root.context().getTrace().size() == nbSamples + 1
|
||||
root.context().getTrace().containsAll(spans)
|
||||
spans[(int) (Math.random() * nbSamples)].context.trace.containsAll(spans)
|
||||
}
|
||||
|
||||
def "global span tags populated on each span"() {
|
||||
setup:
|
||||
System.setProperty("dd.trace.span.tags", tagString)
|
||||
tracer = new DDTracer(writer)
|
||||
def span = tracer.buildSpan("op name").withServiceName("foo").start()
|
||||
tags.putAll([(DDTags.THREAD_NAME): Thread.currentThread().getName(),
|
||||
(DDTags.THREAD_ID) : Thread.currentThread().getId(),
|
||||
(DDTags.SPAN_TYPE) : null])
|
||||
|
||||
expect:
|
||||
span.tags == tags
|
||||
|
||||
where:
|
||||
tagString | tags
|
||||
"" | [:]
|
||||
"in:val:id" | [:]
|
||||
"a:x" | [a: "x"]
|
||||
"a:a,a:b,a:c" | [a: "c"]
|
||||
"a:1,b-c:d" | [a: "1", "b-c": "d"]
|
||||
}
|
||||
}
|
|
@ -120,7 +120,7 @@ class DDSpanTest extends Specification {
|
|||
def between = System.currentTimeMillis()
|
||||
def betweenDur = System.currentTimeMillis() - between
|
||||
span.finish()
|
||||
def total = System.currentTimeMillis() - start
|
||||
def total = Math.max(1, System.currentTimeMillis() - start)
|
||||
|
||||
expect:
|
||||
span.durationNano >= TimeUnit.MILLISECONDS.toNanos(betweenDur)
|
||||
|
|
|
@ -118,4 +118,35 @@ class DDTraceConfigTest extends Specification {
|
|||
"writer" | "agent.host" | "somethingelse" | "DDAgentWriter { api=DDApi { tracesEndpoint=http://somethingelse:8126/v0.3/traces } }"
|
||||
"writer" | "agent.port" | "9999" | "DDAgentWriter { api=DDApi { tracesEndpoint=http://localhost:9999/v0.3/traces } }"
|
||||
}
|
||||
|
||||
def "parsing valid string returns a map"() {
|
||||
expect:
|
||||
DDTraceConfig.parseMap(str) == map
|
||||
|
||||
where:
|
||||
str | map
|
||||
"a:a;" | [a: "a;"]
|
||||
"a:1, a:2, a:3" | [a: "3"]
|
||||
"a:b,c:d" | [a: "b", c: "d"]
|
||||
"key 1!:va|ue_1," | ["key 1!": "va|ue_1"]
|
||||
" key1 :value1 ,\t key2: value2" | [key1: "value1", key2: "value2"]
|
||||
}
|
||||
|
||||
def "parsing an invalid string returns an empty map"() {
|
||||
expect:
|
||||
DDTraceConfig.parseMap(str) == map
|
||||
|
||||
where:
|
||||
str | map
|
||||
null | [:]
|
||||
"" | [:]
|
||||
"1" | [:]
|
||||
"a" | [:]
|
||||
"a:" | [:]
|
||||
"a,1" | [:]
|
||||
"in:val:id" | [:]
|
||||
"a:b:c:d" | [:]
|
||||
"a:b,c,d" | [:]
|
||||
"!a" | [:]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,230 +0,0 @@
|
|||
package datadog.opentracing;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import datadog.trace.api.DDTags;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class DDSpanBuilderTest {
|
||||
|
||||
private DDTracer tracer;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tracer = new DDTracer();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {}
|
||||
|
||||
@Test
|
||||
public void shouldBuildSimpleSpan() {
|
||||
|
||||
final String expectedName = "fakeName";
|
||||
final DDSpan span = tracer.buildSpan(expectedName).withServiceName("foo").startManual();
|
||||
assertThat(span.getOperationName()).isEqualTo(expectedName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBuildMoreComplexSpan() {
|
||||
|
||||
final String expectedName = "fakeName";
|
||||
final Map tags =
|
||||
new HashMap<String, Object>() {
|
||||
{
|
||||
put("1", true);
|
||||
put("2", "fakeString");
|
||||
put("3", 42.0);
|
||||
}
|
||||
};
|
||||
|
||||
DDSpan span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.withTag("1", (Boolean) tags.get("1"))
|
||||
.withTag("2", (String) tags.get("2"))
|
||||
.withTag("3", (Number) tags.get("3"))
|
||||
.startManual();
|
||||
|
||||
assertThat(span.getOperationName()).isEqualTo(expectedName);
|
||||
assertThat(span.getTags()).containsAllEntriesOf(tags);
|
||||
|
||||
// with no tag provided
|
||||
|
||||
span = tracer.buildSpan(expectedName).withServiceName("foo").startManual();
|
||||
|
||||
assertThat(span.getTags()).isNotNull();
|
||||
assertThat(span.getTags().size()).isEqualTo(3);
|
||||
|
||||
// with all custom fields provided
|
||||
final String expectedResource = "fakeResource";
|
||||
final String expectedService = "fakeService";
|
||||
final String expectedType = "fakeType";
|
||||
|
||||
span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.withResourceName(expectedResource)
|
||||
.withServiceName(expectedService)
|
||||
.withErrorFlag()
|
||||
.withSpanType(expectedType)
|
||||
.startManual();
|
||||
|
||||
final DDSpanContext actualContext = span.context();
|
||||
|
||||
assertThat(actualContext.getResourceName()).isEqualTo(expectedResource);
|
||||
assertThat(actualContext.getErrorFlag()).isTrue();
|
||||
assertThat(actualContext.getServiceName()).isEqualTo(expectedService);
|
||||
assertThat(actualContext.getSpanType()).isEqualTo(expectedType);
|
||||
assertThat(actualContext.getTags().get(DDTags.THREAD_NAME))
|
||||
.isEqualTo(Thread.currentThread().getName());
|
||||
assertThat(actualContext.getTags().get(DDTags.THREAD_ID))
|
||||
.isEqualTo(Thread.currentThread().getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldBuildSpanTimestampInNano() {
|
||||
|
||||
// time in micro
|
||||
final long expectedTimestamp = 487517802L * 1000 * 1000L;
|
||||
final String expectedName = "fakeName";
|
||||
|
||||
DDSpan span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.withStartTimestamp(expectedTimestamp)
|
||||
.start();
|
||||
|
||||
// get return nano time
|
||||
assertThat(span.getStartTime()).isEqualTo(expectedTimestamp * 1000L);
|
||||
|
||||
// auto-timestamp in nanoseconds
|
||||
final long tick = System.currentTimeMillis();
|
||||
span = tracer.buildSpan(expectedName).withServiceName("foo").startManual();
|
||||
|
||||
// Give a range of +/- 5 millis
|
||||
assertThat(span.getStartTime())
|
||||
.isBetween(MILLISECONDS.toNanos(tick - 5), MILLISECONDS.toNanos(tick + 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldLinkToParentSpan() {
|
||||
|
||||
final long spanId = 1L;
|
||||
final long expectedParentId = spanId;
|
||||
|
||||
final DDSpanContext mockedContext = mock(DDSpanContext.class);
|
||||
|
||||
when(mockedContext.getSpanId()).thenReturn(spanId);
|
||||
when(mockedContext.getServiceName()).thenReturn("foo");
|
||||
|
||||
final String expectedName = "fakeName";
|
||||
|
||||
final DDSpan span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.asChildOf(mockedContext)
|
||||
.startManual();
|
||||
|
||||
final DDSpanContext actualContext = span.context();
|
||||
|
||||
assertThat(actualContext.getParentId()).isEqualTo(expectedParentId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldInheritOfTheDDParentAttributes() {
|
||||
|
||||
final String expectedName = "fakeName";
|
||||
final String expectedParentServiceName = "fakeServiceName";
|
||||
final String expectedParentResourceName = "fakeResourceName";
|
||||
final String expectedParentType = "fakeType";
|
||||
final String expectedChildServiceName = "fakeServiceName-child";
|
||||
final String expectedChildResourceName = "fakeResourceName-child";
|
||||
final String expectedChildType = "fakeType-child";
|
||||
final String expectedBaggageItemKey = "fakeKey";
|
||||
final String expectedBaggageItemValue = "fakeValue";
|
||||
|
||||
final DDSpan parent =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName("foo")
|
||||
.withResourceName(expectedParentResourceName)
|
||||
.withSpanType(expectedParentType)
|
||||
.startManual();
|
||||
|
||||
parent.setBaggageItem(expectedBaggageItemKey, expectedBaggageItemValue);
|
||||
|
||||
// ServiceName and SpanType are always set by the parent if they are not present in the child
|
||||
DDSpan span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName(expectedParentServiceName)
|
||||
.asChildOf(parent)
|
||||
.startManual();
|
||||
|
||||
assertThat(span.getOperationName()).isEqualTo(expectedName);
|
||||
assertThat(span.getBaggageItem(expectedBaggageItemKey)).isEqualTo(expectedBaggageItemValue);
|
||||
assertThat(span.context().getServiceName()).isEqualTo(expectedParentServiceName);
|
||||
assertThat(span.context().getResourceName()).isNotEqualTo(expectedParentResourceName);
|
||||
assertThat(span.context().getSpanType()).isEqualTo(expectedParentType);
|
||||
|
||||
// ServiceName and SpanType are always overwritten by the child if they are present
|
||||
span =
|
||||
tracer
|
||||
.buildSpan(expectedName)
|
||||
.withServiceName(expectedChildServiceName)
|
||||
.withResourceName(expectedChildResourceName)
|
||||
.withSpanType(expectedChildType)
|
||||
.asChildOf(parent)
|
||||
.startManual();
|
||||
|
||||
assertThat(span.getOperationName()).isEqualTo(expectedName);
|
||||
assertThat(span.getBaggageItem(expectedBaggageItemKey)).isEqualTo(expectedBaggageItemValue);
|
||||
assertThat(span.context().getServiceName()).isEqualTo(expectedChildServiceName);
|
||||
assertThat(span.context().getResourceName()).isEqualTo(expectedChildResourceName);
|
||||
assertThat(span.context().getSpanType()).isEqualTo(expectedChildType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTrackAllSpanInTrace() throws InterruptedException {
|
||||
|
||||
final ArrayList<DDSpan> spans = new ArrayList<>();
|
||||
final int nbSamples = 10;
|
||||
|
||||
// root (aka spans[0]) is the parent
|
||||
// others are just for fun
|
||||
|
||||
final DDSpan root = tracer.buildSpan("fake_O").withServiceName("foo").startManual();
|
||||
spans.add(root);
|
||||
|
||||
Thread.sleep(200);
|
||||
final long tickEnd = System.currentTimeMillis();
|
||||
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
spans.add(
|
||||
tracer
|
||||
.buildSpan("fake_" + i)
|
||||
.withServiceName("foo")
|
||||
.asChildOf(spans.get(i - 1))
|
||||
.startManual());
|
||||
}
|
||||
spans.get(1).finish(tickEnd);
|
||||
|
||||
assertThat(root.context().getTrace()).hasSize(nbSamples + 1);
|
||||
assertThat(root.context().getTrace()).containsAll(spans);
|
||||
assertThat(spans.get((int) (Math.random() * nbSamples)).context().getTrace())
|
||||
.containsAll(spans);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue