getTags() {
+ return this.context().getTags();
+ }
+ @JsonGetter
+ public String getType() {
+ return context.getSpanType();
+ }
+
+ @JsonGetter
+ public int getError() {
+ return context.getErrorFlag() ? 1 : 0;
+ }
+
+ @Override
+ public String toString() {
+ return context.toString();
+ }
+}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/DDSpan.java b/dd-trace/src/main/java/com/datadoghq/trace/DDSpan.java
index 9e1b87610bc..0845e105f40 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/DDSpan.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/DDSpan.java
@@ -1,16 +1,6 @@
package com.datadoghq.trace;
-import com.fasterxml.jackson.annotation.JsonGetter;
-import com.fasterxml.jackson.annotation.JsonIgnore;
import io.opentracing.Span;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.TimeUnit;
/**
* Represents an in-flight span in the opentracing system.
@@ -18,27 +8,7 @@
* Spans are created by the {@link DDTracer#buildSpan}.
* This implementation adds some features according to the DD agent.
*/
-public class DDSpan implements io.opentracing.Span {
-
-
- /**
- * StartTime stores the creation time of the span in milliseconds
- */
- private long startTimeMicro;
- /**
- * StartTimeNano stores the only the nanoseconds for more accuracy
- */
- private long startTimeNano;
- /**
- * The duration in nanoseconds computed using the startTimeMicro and startTimeNano
- */
- private long durationNano;
- /**
- * The context attached to the span
- */
- private final DDSpanContext context;
-
- private final static Logger logger = LoggerFactory.getLogger(DDSpan.class);
+public class DDSpan extends DDBaseSpan implements Span {
/**
* A simple constructor.
@@ -50,276 +20,12 @@ public class DDSpan implements io.opentracing.Span {
protected DDSpan(
long timestampMicro,
DDSpanContext context) {
-
- this.context = context;
-
- // record the start time in nano (current milli + nano delta)
- if (timestampMicro == 0L) {
- this.startTimeMicro = TimeUnit.MILLISECONDS.toMicros(System.currentTimeMillis());
- } else {
- this.startTimeMicro = timestampMicro;
- }
- this.startTimeNano = System.nanoTime();
-
- // track each span of the trace
- this.context.getTrace().add(this);
-
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#finish()
- */
- public void finish() {
- this.durationNano = System.nanoTime() - startTimeNano;
- afterFinish();
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#finish(long)
- */
- public void finish(long stoptimeMicros) {
- this.durationNano = TimeUnit.MICROSECONDS.toNanos(stoptimeMicros - this.startTimeMicro);
- afterFinish();
- }
-
- /**
- * Close the span. If the current span is the parent, check if each child has also been closed
- * If not, warned it
- */
- protected void afterFinish() {
- logger.debug("{} - Closing the span.", this);
-
- // warn if one of the parent's children is not finished
- if (this.isRootSpan()) {
- List spans = this.context.getTrace();
-
- for (Span span : spans) {
- if (((DDSpan) span).getDurationNano() == 0L) {
- logger.warn("{} - The parent span is marked as finished but this span isn't. You have to close each children.", this);
- }
- }
- this.context.getTracer().write(this.context.getTrace());
- logger.debug("{} - Write the trace", this);
- }
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#close()
- */
- public void close() {
- this.finish();
- }
-
- /**
- * Check if the span is the root parent. It means that the traceId is the same as the spanId
- *
- * @return true if root, false otherwise
- */
- private boolean isRootSpan() {
-
- if (context().getTrace().isEmpty()) {
- return false;
- }
- // First item of the array AND tracer set
- DDSpan first = (DDSpan) context().getTrace().get(0);
- return first.context.getSpanId() == this.context.getSpanId() && this.context.getTracer() != null;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#setTag(java.lang.String, java.lang.String)
- */
- public Span setTag(String tag, String value) {
- this.context().setTag(tag, (Object) value);
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#setTag(java.lang.String, boolean)
- */
- public Span setTag(String tag, boolean value) {
- this.context().setTag(tag, (Object) value);
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#setTag(java.lang.String, java.lang.Number)
- */
- public Span setTag(String tag, Number value) {
- this.context().setTag(tag, (Object) value);
- return this;
- }
-
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#context()
- */
- public DDSpanContext context() {
- return this.context;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#setBaggageItem(java.lang.String, java.lang.String)
- */
- public Span setBaggageItem(String key, String value) {
- this.context.setBaggageItem(key, value);
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#getBaggageItem(java.lang.String)
- */
- public String getBaggageItem(String key) {
- return this.context.getBaggageItem(key);
+ super(timestampMicro,context);
}
- /* (non-Javadoc)
- * @see io.opentracing.Span#setOperationName(java.lang.String)
- */
- public Span setOperationName(String operationName) {
- this.context().setOperationName(operationName);
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#log(java.lang.String, java.lang.Object)
- */
- public Span log(Map map) {
- logger.debug("`log` method is not implemented. Doing nothing");
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#log(java.lang.String, java.lang.Object)
- */
- public Span log(long l, Map map) {
- logger.debug("`log` method is not implemented. Doing nothing");
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#log(java.lang.String, java.lang.Object)
- */
- public Span log(String s) {
- logger.debug("`log` method is not implemented. Provided log: {}",s);
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#log(java.lang.String, java.lang.Object)
- */
- public Span log(long l, String s) {
- logger.debug("`log` method is not implemented. Provided log: {}",s);
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#log(java.lang.String, java.lang.Object)
- */
- public Span log(String s, Object o) {
- logger.debug("`log` method is not implemented. Provided log: {}",s);
- return this;
- }
-
- /* (non-Javadoc)
- * @see io.opentracing.Span#log(long, java.lang.String, java.lang.Object)
- */
- public Span log(long l, String s, Object o) {
- logger.debug("`log` method is not implemented. Provided log: {}",s);
- return this;
- }
-
-
- //Getters and JSON serialisation instructions
-
- /**
- * Meta merges baggage and tags (stringified values)
- *
- * @return merged context baggage and tags
- */
- @JsonGetter
- public Map getMeta() {
- Map meta = new HashMap();
- for (Entry entry : context().getBaggageItems().entrySet()) {
- meta.put(entry.getKey(), entry.getValue());
- }
- for (Entry entry : getTags().entrySet()) {
- meta.put(entry.getKey(), String.valueOf(entry.getValue()));
- }
- return meta;
- }
-
- @JsonGetter("start")
- public long getStartTime() {
- return startTimeMicro * 1000L;
- }
-
- @JsonGetter("duration")
- public long getDurationNano() {
- return durationNano;
- }
-
- @JsonGetter("service")
- public String getServiceName() {
- return context.getServiceName();
- }
-
- @JsonGetter("trace_id")
- public long getTraceId() {
- return context.getTraceId();
- }
-
- @JsonGetter("span_id")
- public long getSpanId() {
- return context.getSpanId();
- }
-
- @JsonGetter("parent_id")
- public long getParentId() {
- return context.getParentId();
- }
-
- @JsonGetter("resource")
- public String getResourceName() {
- return context.getResourceName();
- }
-
- @JsonGetter("name")
- public String getOperationName() {
- return context.getOperationName();
- }
-
- @JsonIgnore
- public Map getTags() {
- return this.context().getTags();
- }
- @JsonGetter
- public String getType() {
- return context.getSpanType();
- }
-
- @JsonGetter
- public int getError() {
- return context.getErrorFlag() ? 1 : 0;
- }
-
- @Override
- public String toString() {
- return context.toString();
- }
-
-
- public Span setServiceName(String serviceName) {
- this.context().setServiceName(serviceName);
- return this;
- }
- public Span setResourceName(String resourceName) {
- this.context().setResourceName(resourceName);
- return this;
- }
-
- public Span setSpanType(String type) {
- this.context().setSpanType(type);
- return this;
- }
+ @Override
+ protected DDSpan thisInstance() {
+ return this;
+ }
}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/DDSpanContext.java b/dd-trace/src/main/java/com/datadoghq/trace/DDSpanContext.java
index 8c1dc7b5f57..645021744b4 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/DDSpanContext.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/DDSpanContext.java
@@ -1,12 +1,16 @@
package com.datadoghq.trace;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.fasterxml.jackson.annotation.JsonIgnore;
-import io.opentracing.Span;
-import io.opentracing.tag.Tags;
-import java.util.*;
+import io.opentracing.tag.Tags;
/**
* SpanContext represents Span state that must propagate to descendant Spans and across process boundaries.
@@ -44,7 +48,7 @@ public class DDSpanContext implements io.opentracing.SpanContext {
/**
* The collection of all span related to this one
*/
- private final List trace;
+ private final List> trace;
/**
* Each span have an operation name describing the current span
*/
@@ -70,7 +74,7 @@ public DDSpanContext(
boolean errorFlag,
String spanType,
Map tags,
- List trace,
+ List> trace,
DDTracer tracer) {
this.traceId = traceId;
@@ -93,7 +97,7 @@ public DDSpanContext(
this.tags = tags;
if (trace == null) {
- this.trace = new ArrayList();
+ this.trace = new ArrayList>();
} else {
this.trace = trace;
}
@@ -156,7 +160,7 @@ public Iterable> baggageItems() {
}
@JsonIgnore
- public List getTrace() {
+ public List> getTrace() {
return this.trace;
}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/DDTracer.java b/dd-trace/src/main/java/com/datadoghq/trace/DDTracer.java
index 778b95d4da8..fd66b987ce6 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/DDTracer.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/DDTracer.java
@@ -1,19 +1,27 @@
package com.datadoghq.trace;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.datadoghq.trace.integration.DDSpanContextDecorator;
import com.datadoghq.trace.propagation.Codec;
import com.datadoghq.trace.propagation.HTTPCodec;
import com.datadoghq.trace.sampling.AllSampler;
import com.datadoghq.trace.sampling.Sampler;
-import com.datadoghq.trace.writer.DDAgentWriter;
+import com.datadoghq.trace.writer.LoggingWriter;
import com.datadoghq.trace.writer.Writer;
+
+import io.opentracing.ActiveSpan;
+import io.opentracing.BaseSpan;
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.propagation.Format;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.*;
/**
@@ -45,7 +53,7 @@ public class DDTracer implements io.opentracing.Tracer {
private final CodecRegistry registry;
public static final String UNASSIGNED_DEFAULT_SERVICE_NAME = "unnamed-java-app";
- public static final Writer UNASSIGNED_WRITER = new DDAgentWriter();
+ public static final Writer UNASSIGNED_WRITER = new LoggingWriter();
public static final Sampler UNASSIGNED_SAMPLER = new AllSampler();
/**
@@ -132,11 +140,11 @@ public SpanContext extract(Format format, T carrier) {
*
* @param trace a list of the spans related to the same trace
*/
- public void write(List trace) {
+ public void write(List> trace) {
if (trace.isEmpty()) {
return;
}
- if (this.sampler.sample((DDSpan) trace.get(0))) {
+ if (this.sampler.sample(trace.get(0))) {
this.writer.write(trace);
}
}
@@ -144,6 +152,52 @@ public void write(List trace) {
public void close() {
writer.close();
}
+
+ private final ThreadLocal currentActiveSpan = new ThreadLocal();
+
+ @Override
+ public DDActiveSpan activeSpan() {
+ return currentActiveSpan.get();
+ }
+
+ /**
+ * Set the newly created active span as the active one from the Tracer's perspective
+ *
+ * @param activeSpan
+ */
+ protected void makeActive(DDActiveSpan activeSpan){
+ //We cannot make active a preably deactivated span
+ if(activeSpan!=null && activeSpan.isDeactivated())
+ currentActiveSpan.set(null);
+ else
+ currentActiveSpan.set(activeSpan);
+ }
+
+ /**
+ * Deactivate the current span (if active) and make the parent active (again)
+ *
+ * @param activeSpan
+ */
+ protected void deactivate(DDActiveSpan activeSpan){
+ DDActiveSpan current = activeSpan();
+ if(current==activeSpan){
+ //The parent becomes the active span
+ makeActive(activeSpan.getParent());
+ }
+ }
+
+ @Override
+ public DDActiveSpan makeActive(Span span) {
+ if(!(span instanceof DDSpan))
+ throw new IllegalArgumentException("Cannot transform a non DDSpan into a DDActiveSpan. Provided class: "+span.getClass());
+
+ //Wrap the provided manual span into an active one with the current parent
+ DDActiveSpan activeSpan = new DDActiveSpan(activeSpan(),(DDSpan)span);
+
+ makeActive(activeSpan);
+
+ return activeSpan;
+ }
/**
* Spans are built using this builder
@@ -163,29 +217,56 @@ public class DDSpanBuilder implements SpanBuilder {
private String resourceName;
private boolean errorFlag;
private String spanType;
+ private boolean ignoreActiveSpan = false;
- /**
- * This method actually build the span according to the builder settings
- * DD-Agent requires a serviceName. If it has not been provided, the method will throw a RuntimeException
- *
- * @return An fresh span
- */
- public DDSpan start() {
-
- // build the context
- DDSpanContext context = buildSpanContext();
- DDSpan span = new DDSpan(this.timestamp, context);
+ @Override
+ public SpanBuilder ignoreActiveSpan() {
+ this.ignoreActiveSpan = true;
+ return this;
+ }
- logger.debug("{} - Starting a new span.", span);
+ @Override
+ public DDActiveSpan startActive() {
+ //Set the active span as parent if ignoreActiveSpan==true
+ DDActiveSpan activeParent = null;
+ if(!ignoreActiveSpan){
+ DDActiveSpan current = activeSpan();
+ if(current!=null){
+ activeParent = current;
+
+ //Ensure parent inheritance
+ asChildOf(activeParent);
+ }
+ }
+
+ //Create the active span
+ DDActiveSpan activeSpan = new DDActiveSpan(activeParent,this.timestamp, buildSpanContext());
+ logger.debug("{} - Starting a new active span.", activeSpan);
+
+ makeActive(activeSpan);
+
+ return activeSpan;
+ }
+ @Override
+ public DDSpan startManual() {
+ DDSpan span = new DDSpan(this.timestamp, buildSpanContext());
+ logger.debug("{} - Starting a new manuel span.", span);
return span;
}
+ @Override
+ @Deprecated
+ public DDSpan start() {
+ return startManual();
+ }
+ @Override
public DDTracer.DDSpanBuilder withTag(String tag, Number number) {
return withTag(tag, (Object) number);
}
+ @Override
public DDTracer.DDSpanBuilder withTag(String tag, String string) {
if (tag.equals(DDTags.SERVICE_NAME)) {
return withServiceName(string);
@@ -198,15 +279,17 @@ public DDTracer.DDSpanBuilder withTag(String tag, String string) {
}
}
+ @Override
public DDTracer.DDSpanBuilder withTag(String tag, boolean bool) {
return withTag(tag, (Object) bool);
}
+
public DDSpanBuilder(String operationName) {
this.operationName = operationName;
}
-
+ @Override
public DDTracer.DDSpanBuilder withStartTimestamp(long timestampMillis) {
this.timestamp = timestampMillis;
return this;
@@ -239,15 +322,18 @@ public Iterable> baggageItems() {
return parent.baggageItems();
}
- public DDTracer.DDSpanBuilder asChildOf(Span span) {
+ @Override
+ public DDTracer.DDSpanBuilder asChildOf(BaseSpan> span) {
return asChildOf(span == null ? null : span.context());
}
+ @Override
public DDTracer.DDSpanBuilder asChildOf(SpanContext spanContext) {
this.parent = spanContext;
return this;
}
+ @Override
public DDTracer.DDSpanBuilder addReference(String referenceType, SpanContext spanContext) {
logger.debug("`addReference` method is not implemented. Doing nothing");
return this;
@@ -301,18 +387,18 @@ private DDSpanContext buildSpanContext() {
// some attributes are inherited from the parent
context = new DDSpanContext(
this.parent == null ? generatedId : p.getTraceId(),
- generatedId,
- this.parent == null ? 0L : p.getSpanId(),
- serviceName,
- operationName,
- this.resourceName,
- this.parent == null ? null : p.getBaggageItems(),
- errorFlag,
- spanType,
- this.tags,
- this.parent == null ? null : p.getTrace(),
- DDTracer.this
- );
+ generatedId,
+ this.parent == null ? 0L : p.getSpanId(),
+ serviceName,
+ operationName,
+ this.resourceName,
+ this.parent == null ? null : p.getBaggageItems(),
+ errorFlag,
+ spanType,
+ this.tags,
+ this.parent == null ? null : p.getTrace(),
+ DDTracer.this
+ );
return context;
}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/sampling/ASampler.java b/dd-trace/src/main/java/com/datadoghq/trace/sampling/ASampler.java
index 8b674cfc74e..38d00be2b08 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/sampling/ASampler.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/sampling/ASampler.java
@@ -5,9 +5,7 @@
import java.util.Map.Entry;
import java.util.regex.Pattern;
-import com.datadoghq.trace.DDSpan;
-
-import io.opentracing.Span;
+import com.datadoghq.trace.DDBaseSpan;
public abstract class ASampler implements Sampler{
@@ -15,28 +13,25 @@ public abstract class ASampler implements Sampler{
* Sample tags
*/
protected Map skipTagsPatterns = new HashMap();
-
+
@Override
- public boolean sample(Span span) {
- if(span instanceof DDSpan){
- DDSpan ddSpan = (DDSpan) span;
-
- //Filter by tag values
- for(Entry entry: skipTagsPatterns.entrySet()){
- Object value = ddSpan.getTags().get(entry.getKey());
- if(value != null){
- String strValue = String.valueOf(value);
- Pattern skipPattern = entry.getValue();
- if(skipPattern.matcher(strValue).matches()){
- return false;
- }
+ public boolean sample(DDBaseSpan> span) {
+
+ //Filter by tag values
+ for(Entry entry: skipTagsPatterns.entrySet()){
+ Object value = span.getTags().get(entry.getKey());
+ if(value != null){
+ String strValue = String.valueOf(value);
+ Pattern skipPattern = entry.getValue();
+ if(skipPattern.matcher(strValue).matches()){
+ return false;
}
}
}
-
+
return doSample(span);
}
-
+
/**
* Pattern based skipping of tag values
*
@@ -46,7 +41,7 @@ public boolean sample(Span span) {
public void addSkipTagPattern(String tag,Pattern skipPattern){
skipTagsPatterns.put(tag, skipPattern);
}
-
- protected abstract boolean doSample(Span span);
+
+ protected abstract boolean doSample(DDBaseSpan> span);
}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/sampling/AllSampler.java b/dd-trace/src/main/java/com/datadoghq/trace/sampling/AllSampler.java
index af8cb7d7562..25bd06e8fe0 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/sampling/AllSampler.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/sampling/AllSampler.java
@@ -1,6 +1,6 @@
package com.datadoghq.trace.sampling;
-import io.opentracing.Span;
+import com.datadoghq.trace.DDBaseSpan;
/**
* Sampler that always says yes...
@@ -8,7 +8,7 @@
public class AllSampler extends ASampler {
@Override
- public boolean doSample(Span span) {
+ public boolean doSample(DDBaseSpan> span) {
return true;
}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/sampling/RateSampler.java b/dd-trace/src/main/java/com/datadoghq/trace/sampling/RateSampler.java
index e96c02c9592..48b718299ea 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/sampling/RateSampler.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/sampling/RateSampler.java
@@ -4,10 +4,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.datadoghq.trace.DDBaseSpan;
import com.google.auto.service.AutoService;
-import io.opentracing.Span;
-
/**
* This sampler sample the traces at a predefined rate.
@@ -45,7 +44,7 @@ public RateSampler(double sampleRate) {
}
@Override
- public boolean doSample(Span span) {
+ public boolean doSample(DDBaseSpan> span) {
boolean sample = Math.random() <= this.sampleRate;
logger.debug("{} - Span is sampled: {}", span, sample);
return sample;
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/sampling/Sampler.java b/dd-trace/src/main/java/com/datadoghq/trace/sampling/Sampler.java
index 16f6ed92087..29d158098ca 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/sampling/Sampler.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/sampling/Sampler.java
@@ -1,7 +1,7 @@
package com.datadoghq.trace.sampling;
-import io.opentracing.Span;
+import com.datadoghq.trace.DDBaseSpan;
/**
* Main interface to sample a collection of traces.
@@ -14,6 +14,6 @@ public interface Sampler {
* @param span the parent span with its context
* @return true when the trace/spans has to be reported/written
*/
- boolean sample(Span span);
+ boolean sample(DDBaseSpan> span);
}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java b/dd-trace/src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java
index 6cd53fc93ce..92687b9e1ad 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java
@@ -1,15 +1,20 @@
package com.datadoghq.trace.writer;
-import io.opentracing.Span;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.datadoghq.trace.DDBaseSpan;
import com.google.auto.service.AutoService;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.*;
-
/**
* This writer write provided traces to the a DD agent which is most of time located on the same host.
*
@@ -45,7 +50,7 @@ public class DDAgentWriter implements Writer {
/**
* In memory collection of traces waiting for departure
*/
- private final BlockingQueue> traces;
+ private final BlockingQueue>> traces;
/**
* Async worker that posts the spans to the DD agent
@@ -66,13 +71,13 @@ public DDAgentWriter(DDApi api) {
this.api = api;
tokens = new Semaphore(DEFAULT_MAX_SPANS);
- traces = new ArrayBlockingQueue>(DEFAULT_MAX_SPANS);
+ traces = new ArrayBlockingQueue>>(DEFAULT_MAX_SPANS);
}
/* (non-Javadoc)
* @see com.datadoghq.trace.Writer#write(java.util.List)
*/
- public void write(List trace) {
+ public void write(List> trace) {
//Try to add a new span in the queue
boolean proceed = tokens.tryAcquire(trace.size());
@@ -111,10 +116,10 @@ protected class SpansSendingTask implements Runnable {
public void run() {
while (true) {
try {
- List> payload = new ArrayList>();
+ List>> payload = new ArrayList>>();
//WAIT until a new span comes
- List l = DDAgentWriter.this.traces.take();
+ List> l = DDAgentWriter.this.traces.take();
payload.add(l);
//Drain all spans up to a certain batch suze
@@ -126,7 +131,7 @@ public void run() {
//Compute the number of spans sent
int spansCount = 0;
- for (List trace : payload) {
+ for (List> trace : payload) {
spansCount += trace.size();
}
logger.debug("Async writer just sent {} spans through {} traces", spansCount, payload.size());
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/writer/DDApi.java b/dd-trace/src/main/java/com/datadoghq/trace/writer/DDApi.java
index 1c10f950ed1..21df592c8fb 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/writer/DDApi.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/writer/DDApi.java
@@ -8,12 +8,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.datadoghq.trace.DDBaseSpan;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
-import io.opentracing.Span;
-
/**
* The API pointing to a DD agent
*/
@@ -41,7 +40,7 @@ public DDApi(String host, int port) {
* @param traces the traces to be sent
* @return the staus code returned
*/
- public boolean sendTraces(List> traces) {
+ public boolean sendTraces(List>> traces) {
int status = callPUT(tracesEndpoint, traces);
if (status == 200) {
logger.debug("Succesfully sent {} traces to the DD agent.", traces.size());
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/writer/ListWriter.java b/dd-trace/src/main/java/com/datadoghq/trace/writer/ListWriter.java
new file mode 100644
index 00000000000..e04c529bc3a
--- /dev/null
+++ b/dd-trace/src/main/java/com/datadoghq/trace/writer/ListWriter.java
@@ -0,0 +1,38 @@
+package com.datadoghq.trace.writer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.datadoghq.trace.DDBaseSpan;
+
+/**
+ * List writer used by tests mostly
+ */
+public class ListWriter implements Writer {
+
+ protected List>> list = new ArrayList>>();
+
+ public List>> getList() {
+ return list;
+ }
+
+ public List> firstTrace() {
+ return list.get(0);
+ }
+
+ @Override
+ public void write(List> trace) {
+ list.add(trace);
+ }
+
+ @Override
+ public void start() {
+ list.clear();
+ }
+
+ @Override
+ public void close() {
+ list.clear();
+ }
+
+}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/writer/LoggingWriter.java b/dd-trace/src/main/java/com/datadoghq/trace/writer/LoggingWriter.java
index 86110280275..929008f7755 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/writer/LoggingWriter.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/writer/LoggingWriter.java
@@ -5,17 +5,16 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.datadoghq.trace.DDBaseSpan;
import com.google.auto.service.AutoService;
-import io.opentracing.Span;
-
@AutoService(Writer.class)
public class LoggingWriter implements Writer{
private static final Logger logger = LoggerFactory.getLogger(LoggingWriter.class.getName());
@Override
- public void write(List trace) {
+ public void write(List> trace) {
logger.info("write(trace): {}", trace);
}
diff --git a/dd-trace/src/main/java/com/datadoghq/trace/writer/Writer.java b/dd-trace/src/main/java/com/datadoghq/trace/writer/Writer.java
index 969954f8b13..2fd8ae7af8c 100644
--- a/dd-trace/src/main/java/com/datadoghq/trace/writer/Writer.java
+++ b/dd-trace/src/main/java/com/datadoghq/trace/writer/Writer.java
@@ -2,7 +2,7 @@
import java.util.List;
-import io.opentracing.Span;
+import com.datadoghq.trace.DDBaseSpan;
/**
* A writer is responsible to send collected spans to some place
@@ -14,7 +14,7 @@ public interface Writer {
*
* @param trace the list of spans to write
*/
- void write(List trace);
+ void write(List> trace);
/**
* Start the writer
diff --git a/dd-trace/src/test/java/com/datadoghq/trace/DDActiveSpanTest.java b/dd-trace/src/test/java/com/datadoghq/trace/DDActiveSpanTest.java
new file mode 100644
index 00000000000..4ca4aac46b0
--- /dev/null
+++ b/dd-trace/src/test/java/com/datadoghq/trace/DDActiveSpanTest.java
@@ -0,0 +1,129 @@
+package com.datadoghq.trace;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.datadoghq.trace.writer.ListWriter;
+
+import io.opentracing.ActiveSpan.Continuation;
+
+public class DDActiveSpanTest {
+
+ private ListWriter listWriter = new ListWriter();
+ private DDTracer ddTracer = new DDTracer(listWriter);
+
+ @Before
+ public void setUp(){
+ listWriter.start();
+ }
+
+ @Test
+ public void testThreadContextPropagation() {
+
+ DDActiveSpan span1 = ddTracer.buildSpan("op1").startActive();
+
+ assertThat(span1.getOperationName()).isEqualTo("op1");
+ assertThat(ddTracer.activeSpan()).isEqualTo(span1);
+
+ DDActiveSpan span2 = ddTracer.buildSpan("op2").startActive();
+ assertThat(span2.getOperationName()).isEqualTo("op2");
+ assertThat(span2.getParent()).isEqualTo(span1);
+ assertThat(span2.context().getParentId()).isEqualTo(span1.getSpanId());
+ assertThat(span2.context().getTraceId()).isEqualTo(span1.getTraceId());
+ assertThat(ddTracer.activeSpan()).isEqualTo(span2);
+
+ span2.deactivate();
+ assertThat(ddTracer.activeSpan()).isEqualTo(span1);
+
+ assertThat(listWriter.getList().size()).isEqualTo(0);
+ span1.deactivate();
+ assertThat(ddTracer.activeSpan()).isNull();
+
+ assertThat(listWriter.getList().size()).isEqualTo(1);
+ assertThat(listWriter.getList().get(0).size()).isEqualTo(2);
+ assertThat(listWriter.getList().get(0).get(0)).isEqualTo(span1);
+ assertThat(listWriter.getList().get(0).get(1)).isEqualTo(span2);
+ }
+
+ @Test
+ public void testParentDeactivationFirst() {
+
+ DDActiveSpan span1 = ddTracer.buildSpan("op1").startActive();
+
+ assertThat(span1.getOperationName()).isEqualTo("op1");
+
+ DDActiveSpan span2 = ddTracer.buildSpan("op2").startActive();
+
+ assertThat(ddTracer.activeSpan()).isEqualTo(span2);
+
+ span1.deactivate();
+
+ //If parent span deactivated first => Span 2 remains the active span
+ assertThat(ddTracer.activeSpan()).isEqualTo(span2);
+
+ span2.deactivate();
+
+ //If span2 comes to be deactivated
+ assertThat(ddTracer.activeSpan()).isNull();
+
+ assertThat(listWriter.getList().size()).isEqualTo(1);
+ assertThat(listWriter.getList().get(0).size()).isEqualTo(2);
+ assertThat(listWriter.getList().get(0).get(0)).isEqualTo(span1);
+ assertThat(listWriter.getList().get(0).get(1)).isEqualTo(span2);
+ }
+
+ @Test
+ public void testContinuation() throws Exception{
+ final DDActiveSpan span1 = ddTracer.buildSpan("op1").startActive();
+
+ final Continuation continuation = span1.capture();
+
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ assertThat(ddTracer.activeSpan()).isNull();
+ continuation.activate();
+ assertThat(ddTracer.activeSpan()).isEqualTo(span1);
+ DDActiveSpan span2 = ddTracer.buildSpan("op2").startActive();
+ assertThat(ddTracer.activeSpan()).isEqualTo(span2);
+ span2.deactivate();
+ assertThat(ddTracer.activeSpan()).isEqualTo(span1);
+ }
+ });
+
+ t.start();
+ t.join();
+
+ span1.deactivate();
+ assertThat(ddTracer.activeSpan()).isNull();
+
+ assertThat(listWriter.getList().size()).isEqualTo(1);
+ assertThat(listWriter.getList().get(0).size()).isEqualTo(2);
+ assertThat(listWriter.getList().get(0).get(0)).isEqualTo(span1);
+ }
+
+ @Test
+ public void testMakeSpanActive() {
+ final DDSpan manualSpan = ddTracer.buildSpan("op1").startManual();
+
+ DDActiveSpan span1 = ddTracer.makeActive(manualSpan);
+
+ assertThat(span1.getOperationName()).isEqualTo("op1");
+ assertThat(ddTracer.activeSpan()).isEqualTo(span1);
+ assertThat(manualSpan.context()).isEqualTo(span1.context());
+ assertThat(manualSpan.startTimeNano).isEqualTo(span1.startTimeNano);
+ assertThat(manualSpan.startTimeMicro).isEqualTo(span1.startTimeMicro);
+ }
+
+ @Test
+ public void testCrossContextPropagation() {
+ final DDSpan manualSpan = ddTracer.buildSpan("op1").startManual();
+
+ DDActiveSpan activeSpan = ddTracer.buildSpan("op2").asChildOf(manualSpan.context()).startActive();
+
+ assertThat(activeSpan.getParentId()).isEqualTo(manualSpan.getSpanId());
+ assertThat(activeSpan.getTraceId()).isEqualTo(manualSpan.getTraceId());
+ }
+}
diff --git a/dd-trace/src/test/java/com/datadoghq/trace/DDSpanBuilderTest.java b/dd-trace/src/test/java/com/datadoghq/trace/DDSpanBuilderTest.java
index 75355be2406..06ea01a704e 100644
--- a/dd-trace/src/test/java/com/datadoghq/trace/DDSpanBuilderTest.java
+++ b/dd-trace/src/test/java/com/datadoghq/trace/DDSpanBuilderTest.java
@@ -131,9 +131,7 @@ public void shouldLinkToParentSpan() {
final long expectedParentId = spanId;
DDSpanContext mockedContext = mock(DDSpanContext.class);
- DDSpan mockedSpan = mock(DDSpan.class);
-
- when(mockedSpan.context()).thenReturn(mockedContext);
+
when(mockedContext.getSpanId()).thenReturn(spanId);
when(mockedContext.getServiceName()).thenReturn("foo");
@@ -142,13 +140,12 @@ public void shouldLinkToParentSpan() {
DDSpan span = tracer
.buildSpan(expectedName)
.withServiceName("foo")
- .asChildOf(mockedSpan)
+ .asChildOf(mockedContext)
.start();
DDSpanContext actualContext = span.context();
assertThat(actualContext.getParentId()).isEqualTo(expectedParentId);
-
}
diff --git a/dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializerTest.java b/dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializationTest.java
similarity index 78%
rename from dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializerTest.java
rename to dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializationTest.java
index 434711eb0ea..ff7cbc5dc0f 100644
--- a/dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializerTest.java
+++ b/dd-trace/src/test/java/com/datadoghq/trace/DDSpanSerializationTest.java
@@ -10,11 +10,12 @@
import com.fasterxml.jackson.databind.ObjectMapper;
-public class DDSpanSerializerTest {
+public class DDSpanSerializationTest {
ObjectMapper serializer;
DDSpan span;
+ DDActiveSpan activeSpan;
@Before
public void setUp() throws Exception {
@@ -42,8 +43,10 @@ public void setUp() throws Exception {
span = new DDSpan(
100L,
context);
-
span.finish(133L);
+
+ activeSpan = new DDActiveSpan(null,100L,context);
+ activeSpan.deactivate();
serializer = new ObjectMapper();
}
@@ -55,9 +58,16 @@ public void test() throws Exception {
// FIXME At the moment, just compare the string sizes
try {
assertThat(serializer.writeValueAsString(span).length()).isEqualTo(expected.length());
+
} catch (AssertionError e) {
assertThat(serializer.writeValueAsString(span)).isEqualTo(expected);
}
+
+// try {
+// assertThat(serializer.writeValueAsString(activeSpan).length()).isEqualTo(expected.length());
+// } catch (AssertionError e) {
+// assertThat(serializer.writeValueAsString(activeSpan)).isEqualTo(expected);
+// }
}
}
diff --git a/dd-trace/src/test/java/com/datadoghq/trace/DDTracerTest.java b/dd-trace/src/test/java/com/datadoghq/trace/DDTracerTest.java
index 691cd5e4fb8..866eddcd733 100644
--- a/dd-trace/src/test/java/com/datadoghq/trace/DDTracerTest.java
+++ b/dd-trace/src/test/java/com/datadoghq/trace/DDTracerTest.java
@@ -16,6 +16,7 @@
import com.datadoghq.trace.sampling.RateSampler;
import com.datadoghq.trace.writer.Writer;
+import io.opentracing.BaseSpan;
import io.opentracing.Span;
@@ -34,7 +35,7 @@ public void write() throws Exception {
.thenReturn(true)
.thenReturn(false);
- List spans = new ArrayList();
+ List> spans = new ArrayList>();
spans.add(span);
spans.add(span);
spans.add(span);
diff --git a/dd-trace/src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java b/dd-trace/src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java
index f5da256de7c..80edb066381 100644
--- a/dd-trace/src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java
+++ b/dd-trace/src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java
@@ -11,6 +11,7 @@
import org.junit.Before;
import org.junit.Test;
+import com.datadoghq.trace.DDBaseSpan;
import com.datadoghq.trace.DDSpan;
import com.datadoghq.trace.DDTracer;
import com.datadoghq.trace.writer.DDAgentWriter;
@@ -22,7 +23,7 @@ public class DDAgentWriterTest {
DDSpan parent = null;
DDApi mockedAPI = null;
- List> traces = new ArrayList>();
+ List>> traces = new ArrayList>>();
DDAgentWriter ddAgentWriter = null;
@Before
diff --git a/dd-trace/src/test/resources/logback.xml b/dd-trace/src/test/resources/logback.xml
index 607d3e615d8..c331c42dfdb 100644
--- a/dd-trace/src/test/resources/logback.xml
+++ b/dd-trace/src/test/resources/logback.xml
@@ -9,7 +9,7 @@
-
+
diff --git a/pom.xml b/pom.xml
index 24b11f37afb..2441ecdd2bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.datadoghq
dd-trace-java
- 0.0.7-SNAPSHOT
+ 0.0.8-SNAPSHOT
pom
@@ -44,7 +44,7 @@
- 0.22.0
+ 0.30.0
1.7