adding ability to report all span in the trace

This commit is contained in:
Guillaume Polaert 2017-04-27 09:44:05 +02:00
parent 1137854b41
commit 162656296f
5 changed files with 53 additions and 44 deletions

View File

@ -1,12 +1,11 @@
package com.datadoghq.trace.impl; package com.datadoghq.trace.impl;
import java.util.LinkedHashMap; import java.time.Clock;
import java.util.LinkedHashSet; import java.util.*;
import java.util.Map;
import java.util.Optional;
import com.fasterxml.jackson.annotation.JsonGetter; import com.fasterxml.jackson.annotation.JsonGetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.opentracing.Span; import io.opentracing.Span;
import io.opentracing.SpanContext; import io.opentracing.SpanContext;
@ -16,30 +15,28 @@ public class DDSpan implements io.opentracing.Span {
protected final Tracer tracer; protected final Tracer tracer;
protected String operationName; protected String operationName;
protected Map<String, Object> tags; protected Map<String, Object> tags;
protected long startTime; protected long startTimeNano;
protected long startTimeNano; // Only used to measure nano time durations
protected long durationNano; protected long durationNano;
protected final DDSpanContext context; protected final DDSpanContext context;
protected final LinkedHashSet<Span> traces; protected final ArrayList<Span> trace;
DDSpan( DDSpan(
Tracer tracer, Tracer tracer,
String operationName, String operationName,
LinkedHashSet<Span> traces, ArrayList<Span> trace,
Map<String, Object> tags, Map<String, Object> tags,
Long timestamp, Long timestampMilliseconds,
DDSpanContext context) { DDSpanContext context) {
this.tracer = tracer; this.tracer = tracer;
this.operationName = operationName; this.operationName = operationName;
this.traces = Optional.ofNullable(traces).orElse(new LinkedHashSet<>()); this.trace = Optional.ofNullable(trace).orElse(new ArrayList<>());
this.tags = tags; this.tags = tags;
this.startTime = System.currentTimeMillis() * 1000000; this.startTimeNano = Optional.ofNullable(timestampMilliseconds).orElse(Clock.systemUTC().millis()) * 1000000L;
this.startTimeNano = System.nanoTime();
this.context = context; this.context = context;
// track each span of the trace // track each span of the trace
this.traces.add(this); this.trace.add(this);
} }
@ -48,15 +45,19 @@ public class DDSpan implements io.opentracing.Span {
} }
public void finish() { public void finish() {
finish(System.nanoTime()); finish(Clock.systemUTC().millis());
} }
public void finish(long stopTimeMicro) { public void finish(long stopTimeMillis) {
this.durationNano = stopTimeMicro * 1000L - startTime; this.durationNano = (stopTimeMillis * 1000000L - startTimeNano);
if (this.isRootSpan()) { if (this.isRootSpan()) {
this.traces.stream() this.trace.stream()
.filter(s -> ((DDSpanContext) s.context()).getSpanId() != ((DDSpanContext) this.context()).getSpanId()) .filter(s -> {
.forEach(s -> s.finish()); boolean isSelf = ((DDSpanContext) s.context()).getSpanId() == ((DDSpanContext) this.context()).getSpanId();
boolean isFinished = ((DDSpan) s).getDurationNano() != 0L;
return !isSelf && !isFinished;
})
.forEach(Span::finish);
} }
} }
@ -142,7 +143,7 @@ public class DDSpan implements io.opentracing.Span {
@JsonGetter(value = "start") @JsonGetter(value = "start")
public long getStartTime() { public long getStartTime() {
return startTime; return startTimeNano;
} }
@JsonGetter(value = "duration") @JsonGetter(value = "duration")
@ -182,7 +183,8 @@ public class DDSpan implements io.opentracing.Span {
return context.getErrorFlag() ? 1 : 0; return context.getErrorFlag() ? 1 : 0;
} }
public LinkedHashSet<Span> getTraces() { @JsonIgnore
return traces; public ArrayList<Span> getTrace() {
return trace;
} }
} }

View File

@ -73,8 +73,8 @@ public class Tracer implements io.opentracing.Tracer {
return this; return this;
} }
public Tracer.SpanBuilder withStartTimestamp(long timestamp) { public Tracer.SpanBuilder withStartTimestamp(long timestampMillis) {
this.timestamp = timestamp; this.timestamp = timestampMillis;
return this; return this;
} }
@ -105,15 +105,15 @@ public class Tracer implements io.opentracing.Tracer {
DDSpanContext context = buildTheSpanContext(); DDSpanContext context = buildTheSpanContext();
logger.startNewSpan(this.operationName, context.getSpanId()); logger.startNewSpan(this.operationName, context.getSpanId());
LinkedHashSet traces = null; ArrayList<Span> trace = null;
if (this.parent != null) { if (this.parent != null) {
traces = parent.getTraces(); trace = parent.getTrace();
} }
return new DDSpan( return new DDSpan(
Tracer.this, Tracer.this,
this.operationName, this.operationName,
traces, trace,
this.tags, this.tags,
this.timestamp, this.timestamp,
context); context);

View File

@ -16,7 +16,7 @@ import io.opentracing.Span;
public class DDApi { public class DDApi {
protected static final String TRACES_ENDPOINT = "/v0.3/traces"; protected static final String TRACES_ENDPOINT = "/v0.3/trace";
protected static final String SERVICES_ENDPOINT = "/v0.3/services"; protected static final String SERVICES_ENDPOINT = "/v0.3/services";
protected final String host; protected final String host;
@ -121,7 +121,7 @@ public class DDApi {
// String service = "{\"service_name\": {\"app\": \"service-name\",\"app_type\": \"web\"}}"; // String service = "{\"service_name\": {\"app\": \"service-name\",\"app_type\": \"web\"}}";
// System.out.println("Pushed service: "+api.callPUT(api.servicesEndpoint, service)); // System.out.println("Pushed service: "+api.callPUT(api.servicesEndpoint, service));
System.out.println("Pushed traces: "+api.sendTraces(traces)); System.out.println("Pushed trace: "+api.sendTraces(traces));
} }
} }

View File

@ -1,10 +1,10 @@
package com.datadoghq.trace.impl; package com.datadoghq.trace.impl;
import io.opentracing.References;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.time.Clock;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -101,16 +101,16 @@ public class DDSpanBuilderTest {
.withStartTimestamp(expectedTimestamp) .withStartTimestamp(expectedTimestamp)
.start(); .start();
assertThat(span.getStartTime()).isEqualTo(expectedTimestamp); assertThat(span.getStartTime()).isEqualTo(expectedTimestamp * 1000000L);
// auto-timestamp in nanoseconds // auto-timestamp in nanoseconds
long tick = System.nanoTime(); long tick = Clock.systemUTC().millis() * 1000000L;
span = (DDSpan) tracer span = (DDSpan) tracer
.buildSpan(expectedName) .buildSpan(expectedName)
.start(); .start();
// between now and now + 100ms // between now and now + 100ms
assertThat(span.getStartTime()).isBetween(tick, tick + 100 * 1000); assertThat(span.getStartTime()).isBetween(tick, tick + 100 * 1000000L);
} }
@ -171,25 +171,37 @@ public class DDSpanBuilderTest {
} }
@Test @Test
public void shouldTrackAllSpanInTrace() { public void shouldTrackAllSpanInTrace() throws InterruptedException {
ArrayList<DDSpan> spans = new ArrayList<>(); ArrayList<DDSpan> spans = new ArrayList<>();
final int nbSamples = 10; final int nbSamples = 10;
// root (aka spans[0]) is the parent
// spans[1] has a predictable duration
// others are just for fun
DDSpan root = (DDSpan) tracer.buildSpan("fake_O").start(); DDSpan root = (DDSpan) tracer.buildSpan("fake_O").start();
spans.add(root); spans.add(root);
for (int i = 1; i <= 10; i++) { long tickStart = Clock.systemUTC().millis();
spans.add((DDSpan) tracer.buildSpan("fake_" + 1).asChildOf(spans.get(0)).withStartTimestamp(tickStart).start());
for (int i = 2; i <= 10; i++) {
spans.add((DDSpan) tracer.buildSpan("fake_" + i).asChildOf(spans.get(i - 1)).start()); spans.add((DDSpan) tracer.buildSpan("fake_" + i).asChildOf(spans.get(i - 1)).start());
} }
assertThat(root.getTraces()).hasSize(nbSamples + 1); Thread.sleep(300);
assertThat(root.getTraces()).containsAll(spans); long tickEnd = Clock.systemUTC().millis();
assertThat(spans.get((int) (Math.random() * nbSamples)).getTraces()).containsAll(spans);
spans.get(1).finish(tickEnd);
assertThat(root.getTrace()).hasSize(nbSamples + 1);
assertThat(root.getTrace()).containsAll(spans);
assertThat(spans.get((int) (Math.random() * nbSamples)).getTrace()).containsAll(spans);
root.finish(); root.finish();
//TODO Check order //TODO Check order
//assertThat(root.getTraces()).containsExactly(spans) //assertThat(root.getTrace()).containsExactly(spans)
assertThat(spans.get(1).durationNano).isEqualTo((tickEnd - tickStart) * 1000000L);
spans.forEach(span -> assertThat(span.getDurationNano()).isNotNull()); spans.forEach(span -> assertThat(span.getDurationNano()).isNotNull());
spans.forEach(span -> assertThat(span.getDurationNano()).isNotZero()); spans.forEach(span -> assertThat(span.getDurationNano()).isNotZero());

View File

@ -1,13 +1,8 @@
package com.datadoghq.trace.impl; package com.datadoghq.trace.impl;
import io.opentracing.Span;
import org.junit.Test; import org.junit.Test;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.floatThat;
import static org.mockito.Mockito.mock;
public class DDSpanTest { public class DDSpanTest {