Merge branch 'dev' of github.com:DataDog/dd-trace-java into dev

This commit is contained in:
renaudboutet 2017-05-29 17:33:13 +02:00
commit 8e14d51d58
8 changed files with 324 additions and 310 deletions

View File

@ -4,6 +4,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import java.util.List; import java.util.List;
import io.opentracing.tag.Tags;
import org.junit.Test; import org.junit.Test;
import com.datadoghq.trace.DDTracer; import com.datadoghq.trace.DDTracer;
@ -18,18 +19,22 @@ public class TracerResolverTest {
DDTracerResolver tracerResolver = new DDTracerResolver(); DDTracerResolver tracerResolver = new DDTracerResolver();
DDTracer tracer = (DDTracer) tracerResolver.resolve(); DDTracer tracer = (DDTracer) tracerResolver.resolve();
List<DDSpanContextDecorator> decorators = tracer.getSpanContextDecorators(); // for HTTP decorators
List<DDSpanContextDecorator> decorators = tracer.getSpanContextDecorators(Tags.COMPONENT.getKey());
assertThat(decorators.size()).isEqualTo(2); assertThat(decorators.size()).isEqualTo(2);
DDSpanContextDecorator decorator = decorators.get(0); DDSpanContextDecorator decorator = decorators.get(0);
assertThat(decorator.getClass()).isEqualTo(HTTPComponent.class); assertThat(decorator.getClass()).isEqualTo(HTTPComponent.class);
HTTPComponent httpServiceDecorator = (HTTPComponent) decorator; HTTPComponent httpServiceDecorator = (HTTPComponent) decorator;
assertThat(httpServiceDecorator.getMatchingTag()).isEqualTo("component"); assertThat(httpServiceDecorator.getMatchingTag()).isEqualTo("component");
assertThat(httpServiceDecorator.getMatchingValue()).isEqualTo("hello"); assertThat(httpServiceDecorator.getMatchingValue()).isEqualTo("hello");
assertThat(httpServiceDecorator.getSetValue()).isEqualTo("world"); assertThat(httpServiceDecorator.getSetValue()).isEqualTo("world");
decorator = decorators.get(1); // for URL decorators
decorators = tracer.getSpanContextDecorators(Tags.HTTP_URL.getKey());
assertThat(decorators.size()).isEqualTo(1);
decorator = decorators.get(0);
assertThat(decorator.getClass()).isEqualTo(URLAsResourceName.class); assertThat(decorator.getClass()).isEqualTo(URLAsResourceName.class);
} }

View File

@ -2,4 +2,7 @@ decorators:
- type: HTTPComponent - type: HTTPComponent
matchingValue: hello matchingValue: hello
setValue: world setValue: world
- type: HTTPComponent
matchingValue: foo
setValue: bar
- type: URLAsResourceName - type: URLAsResourceName

View File

@ -175,7 +175,7 @@ public class DDSpanContext implements io.opentracing.SpanContext {
this.tags.put(tag, value); this.tags.put(tag, value);
//Call decorators //Call decorators
for (DDSpanContextDecorator decorator : tracer.getSpanContextDecorators()) { for (DDSpanContextDecorator decorator : tracer.getSpanContextDecorators(tag)) {
decorator.afterSetTag(this, tag, value); decorator.afterSetTag(this, tag, value);
} }
//Error management //Error management

View File

@ -1,14 +1,5 @@
package com.datadoghq.trace; 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.integration.DDSpanContextDecorator;
import com.datadoghq.trace.propagation.Codec; import com.datadoghq.trace.propagation.Codec;
import com.datadoghq.trace.propagation.HTTPCodec; import com.datadoghq.trace.propagation.HTTPCodec;
@ -16,10 +7,13 @@ import com.datadoghq.trace.sampling.AllSampler;
import com.datadoghq.trace.sampling.Sampler; import com.datadoghq.trace.sampling.Sampler;
import com.datadoghq.trace.writer.DDAgentWriter; import com.datadoghq.trace.writer.DDAgentWriter;
import com.datadoghq.trace.writer.Writer; import com.datadoghq.trace.writer.Writer;
import io.opentracing.Span; import io.opentracing.Span;
import io.opentracing.SpanContext; import io.opentracing.SpanContext;
import io.opentracing.propagation.Format; import io.opentracing.propagation.Format;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/** /**
@ -27,316 +21,336 @@ import io.opentracing.propagation.Format;
*/ */
public class DDTracer implements io.opentracing.Tracer { public class DDTracer implements io.opentracing.Tracer {
/** /**
* Writer is an charge of reporting traces and spans to the desired endpoint * Writer is an charge of reporting traces and spans to the desired endpoint
*/ */
private Writer writer; private Writer writer;
/** /**
* Sampler defines the sampling policy in order to reduce the number of traces for instance * Sampler defines the sampling policy in order to reduce the number of traces for instance
*/ */
private final Sampler sampler; private final Sampler sampler;
/** /**
* Default service name if none provided on the trace or span * Default service name if none provided on the trace or span
*/ */
private final String defaultServiceName; private final String defaultServiceName;
/** /**
* Span context decorators * Span context decorators
*/ */
private final List<DDSpanContextDecorator> spanContextDecorators = new ArrayList<DDSpanContextDecorator>(); private final Map<String, List<DDSpanContextDecorator>> spanContextDecorators = new HashMap<>();
private final static Logger logger = LoggerFactory.getLogger(DDTracer.class); private final static Logger logger = LoggerFactory.getLogger(DDTracer.class);
private final CodecRegistry registry; private final CodecRegistry registry;
public static final String UNASSIGNED_DEFAULT_SERVICE_NAME = "unnamed-java-app"; 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 DDAgentWriter();
public static final Sampler UNASSIGNED_SAMPLER = new AllSampler(); public static final Sampler UNASSIGNED_SAMPLER = new AllSampler();
/** /**
* Default constructor, trace/spans are logged, no trace/span dropped * Default constructor, trace/spans are logged, no trace/span dropped
*/ */
public DDTracer() { public DDTracer() {
this(UNASSIGNED_WRITER); this(UNASSIGNED_WRITER);
} }
public DDTracer(Writer writer) { public DDTracer(Writer writer) {
this(writer, new AllSampler()); this(writer, new AllSampler());
} }
public DDTracer(Writer writer, Sampler sampler) { public DDTracer(Writer writer, Sampler sampler) {
this(UNASSIGNED_DEFAULT_SERVICE_NAME, writer, sampler); this(UNASSIGNED_DEFAULT_SERVICE_NAME, writer, sampler);
} }
public DDTracer(String defaultServiceName, Writer writer, Sampler sampler) { public DDTracer(String defaultServiceName, Writer writer, Sampler sampler) {
this.defaultServiceName = defaultServiceName; this.defaultServiceName = defaultServiceName;
this.writer = writer; this.writer = writer;
this.writer.start(); this.writer.start();
this.sampler = sampler; this.sampler = sampler;
registry = new CodecRegistry(); registry = new CodecRegistry();
registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec()); registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec());
} }
/** /**
* Returns the list of span context decorators * Returns the list of span context decorators
* *
* @return the list of span context decorators * @return the list of span context decorators
*/ */
public List<DDSpanContextDecorator> getSpanContextDecorators() { public List<DDSpanContextDecorator> getSpanContextDecorators(String tag) {
return Collections.unmodifiableList(spanContextDecorators); List<DDSpanContextDecorator> decorators = Collections.emptyList();
} String key = getHashKey(tag);
/** if (spanContextDecorators.containsKey(key)) {
* Add a new decorator in the list ({@link DDSpanContextDecorator}) decorators = Collections.unmodifiableList(spanContextDecorators.get(key));
* }
* @param decorator The decorator in the list
*/
public void addDecorator(DDSpanContextDecorator decorator) {
spanContextDecorators.add(decorator);
}
public DDSpanBuilder buildSpan(String operationName) { return decorators;
return new DDSpanBuilder(operationName); }
}
/**
* Add a new decorator in the list ({@link DDSpanContextDecorator})
*
* @param decorator The decorator in the list
*/
public void addDecorator(DDSpanContextDecorator decorator) {
String key = getHashKey(decorator.getMatchingTag());
List<DDSpanContextDecorator> list = spanContextDecorators.get(key);
if (list == null) {
list = new ArrayList<>();
}
list.add(decorator);
spanContextDecorators.put(key, list);
}
public <T> void inject(SpanContext spanContext, Format<T> format, T carrier) { public DDSpanBuilder buildSpan(String operationName) {
return new DDSpanBuilder(operationName);
Codec<T> codec = registry.get(format); }
if (codec == null) {
logger.warn("Unsupported format for propagation - {}", format.getClass().getName());
} else {
codec.inject((DDSpanContext) spanContext, carrier);
}
}
public <T> SpanContext extract(Format<T> format, T carrier) {
Codec<T> codec = registry.get(format);
if (codec == null) {
logger.warn("Unsupported format for propagation - {}", format.getClass().getName());
} else {
return codec.extract(carrier);
}
return null;
}
/** public <T> void inject(SpanContext spanContext, Format<T> format, T carrier) {
* We use the sampler to know if the trace has to be reported/written.
* The sampler is called on the first span (root span) of the trace.
* If the trace is marked as a sample, we report it.
*
* @param trace a list of the spans related to the same trace
*/
public void write(List<Span> trace) {
if (trace.isEmpty()) {
return;
}
if (this.sampler.sample((DDSpan) trace.get(0))) {
this.writer.write(trace);
}
}
public void close() { Codec<T> codec = registry.get(format);
writer.close(); if (codec == null) {
} logger.warn("Unsupported format for propagation - {}", format.getClass().getName());
} else {
codec.inject((DDSpanContext) spanContext, carrier);
}
}
/** public <T> SpanContext extract(Format<T> format, T carrier) {
* Spans are built using this builder
*/
public class DDSpanBuilder implements SpanBuilder {
/** Codec<T> codec = registry.get(format);
* Each span must have an operationName according to the opentracing specification if (codec == null) {
*/ logger.warn("Unsupported format for propagation - {}", format.getClass().getName());
private String operationName; } else {
return codec.extract(carrier);
// Builder attributes }
private Map<String, Object> tags = Collections.emptyMap(); return null;
private long timestamp; }
private SpanContext parent;
private String serviceName;
private String resourceName;
private boolean errorFlag;
private String spanType;
/**
* 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);
logger.debug("{} - Starting a new span.", span);
return span;
}
public DDTracer.DDSpanBuilder withTag(String tag, Number number) { /**
return withTag(tag, (Object) number); * We use the sampler to know if the trace has to be reported/written.
} * The sampler is called on the first span (root span) of the trace.
* If the trace is marked as a sample, we report it.
*
* @param trace a list of the spans related to the same trace
*/
public void write(List<Span> trace) {
if (trace.isEmpty()) {
return;
}
if (this.sampler.sample((DDSpan) trace.get(0))) {
this.writer.write(trace);
}
}
public DDTracer.DDSpanBuilder withTag(String tag, String string) { public void close() {
if (tag.equals(DDTags.SERVICE_NAME)) { writer.close();
return withServiceName(string); }
} else if (tag.equals(DDTags.RESOURCE_NAME)) {
return withResourceName(string);
} else if (tag.equals(DDTags.SPAN_TYPE)) {
return withSpanType(string);
} else {
return withTag(tag, (Object) string);
}
}
public DDTracer.DDSpanBuilder withTag(String tag, boolean bool) { /**
return withTag(tag, (Object) bool); * Spans are built using this builder
} */
public class DDSpanBuilder implements SpanBuilder {
public DDSpanBuilder(String operationName) { /**
this.operationName = operationName; * Each span must have an operationName according to the opentracing specification
} */
private String operationName;
// Builder attributes
private Map<String, Object> tags = Collections.emptyMap();
private long timestamp;
private SpanContext parent;
private String serviceName;
private String resourceName;
private boolean errorFlag;
private String spanType;
/**
* 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);
logger.debug("{} - Starting a new span.", span);
return span;
}
public DDTracer.DDSpanBuilder withStartTimestamp(long timestampMillis) { public DDTracer.DDSpanBuilder withTag(String tag, Number number) {
this.timestamp = timestampMillis; return withTag(tag, (Object) number);
return this; }
}
public DDTracer.DDSpanBuilder withServiceName(String serviceName) { public DDTracer.DDSpanBuilder withTag(String tag, String string) {
this.serviceName = serviceName; if (tag.equals(DDTags.SERVICE_NAME)) {
return this; return withServiceName(string);
} } else if (tag.equals(DDTags.RESOURCE_NAME)) {
return withResourceName(string);
} else if (tag.equals(DDTags.SPAN_TYPE)) {
return withSpanType(string);
} else {
return withTag(tag, (Object) string);
}
}
public DDTracer.DDSpanBuilder withResourceName(String resourceName) { public DDTracer.DDSpanBuilder withTag(String tag, boolean bool) {
this.resourceName = resourceName; return withTag(tag, (Object) bool);
return this; }
}
public DDTracer.DDSpanBuilder withErrorFlag() { public DDSpanBuilder(String operationName) {
this.errorFlag = true; this.operationName = operationName;
return this; }
}
public DDTracer.DDSpanBuilder withSpanType(String spanType) {
this.spanType = spanType;
return this;
}
public Iterable<Map.Entry<String, String>> baggageItems() { public DDTracer.DDSpanBuilder withStartTimestamp(long timestampMillis) {
if (parent == null) { this.timestamp = timestampMillis;
return Collections.emptyList(); return this;
} }
return parent.baggageItems();
}
public DDTracer.DDSpanBuilder asChildOf(Span span) { public DDTracer.DDSpanBuilder withServiceName(String serviceName) {
return asChildOf(span==null? null : span.context()); this.serviceName = serviceName;
} return this;
}
public DDTracer.DDSpanBuilder asChildOf(SpanContext spanContext) { public DDTracer.DDSpanBuilder withResourceName(String resourceName) {
this.parent = spanContext; this.resourceName = resourceName;
return this; return this;
} }
public DDTracer.DDSpanBuilder addReference(String referenceType, SpanContext spanContext) { public DDTracer.DDSpanBuilder withErrorFlag() {
logger.debug("`addReference` method is not implemented. Doing nothing"); this.errorFlag = true;
return this; return this;
} }
// Private methods public DDTracer.DDSpanBuilder withSpanType(String spanType) {
private DDTracer.DDSpanBuilder withTag(String tag, Object value) { this.spanType = spanType;
if (this.tags.isEmpty()) { return this;
this.tags = new HashMap<String, Object>(); }
}
this.tags.put(tag, value);
return this;
}
private long generateNewId() { public Iterable<Map.Entry<String, String>> baggageItems() {
return System.nanoTime(); if (parent == null) {
} return Collections.emptyList();
}
return parent.baggageItems();
}
/** public DDTracer.DDSpanBuilder asChildOf(Span span) {
* Build the SpanContext, if the actual span has a parent, the following attributes must be propagated: return asChildOf(span == null ? null : span.context());
* - ServiceName }
* - Baggage
* - Trace (a list of all spans related)
* - SpanType
*
* @return the context
*/
private DDSpanContext buildSpanContext() {
long generatedId = generateNewId();
DDSpanContext context;
DDSpanContext p = this.parent != null ? (DDSpanContext) this.parent : null;
String spanType = this.spanType; public DDTracer.DDSpanBuilder asChildOf(SpanContext spanContext) {
if (spanType == null && this.parent != null) { this.parent = spanContext;
spanType = p.getSpanType(); return this;
} }
String serviceName = this.serviceName; public DDTracer.DDSpanBuilder addReference(String referenceType, SpanContext spanContext) {
if (serviceName == null) { logger.debug("`addReference` method is not implemented. Doing nothing");
if (p != null && p.getServiceName() != null) { return this;
serviceName = p.getServiceName(); }
} else {
serviceName = defaultServiceName;
}
}
String operationName = this.operationName != null ? this.operationName : this.resourceName; // Private methods
private DDTracer.DDSpanBuilder withTag(String tag, Object value) {
if (this.tags.isEmpty()) {
this.tags = new HashMap<String, Object>();
}
this.tags.put(tag, value);
return this;
}
//this.operationName, this.tags, private long generateNewId() {
return System.nanoTime();
}
// some attributes are inherited from the parent /**
context = new DDSpanContext( * Build the SpanContext, if the actual span has a parent, the following attributes must be propagated:
this.parent == null ? generatedId : p.getTraceId(), * - ServiceName
generatedId, * - Baggage
this.parent == null ? 0L : p.getSpanId(), * - Trace (a list of all spans related)
serviceName, * - SpanType
operationName, *
this.resourceName, * @return the context
this.parent == null ? null : p.getBaggageItems(), */
errorFlag, private DDSpanContext buildSpanContext() {
spanType, long generatedId = generateNewId();
this.tags, DDSpanContext context;
this.parent == null ? null : p.getTrace(), DDSpanContext p = this.parent != null ? (DDSpanContext) this.parent : null;
DDTracer.this
);
return context; String spanType = this.spanType;
} if (spanType == null && this.parent != null) {
spanType = p.getSpanType();
}
} String serviceName = this.serviceName;
if (serviceName == null) {
if (p != null && p.getServiceName() != null) {
serviceName = p.getServiceName();
} else {
serviceName = defaultServiceName;
}
}
private static class CodecRegistry { String operationName = this.operationName != null ? this.operationName : this.resourceName;
private final Map<Format<?>, Codec<?>> codecs = new HashMap<Format<?>, Codec<?>>(); //this.operationName, this.tags,
@SuppressWarnings("unchecked") // 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
);
return context;
}
}
private String getHashKey(String tag) {
return tag;
}
private static class CodecRegistry {
private final Map<Format<?>, Codec<?>> codecs = new HashMap<Format<?>, Codec<?>>();
@SuppressWarnings("unchecked")
<T> Codec<T> get(Format<T> format) { <T> Codec<T> get(Format<T> format) {
return (Codec<T>) codecs.get(format); return (Codec<T>) codecs.get(format);
} }
public <T> void register(Format<T> format, Codec<T> codec) { public <T> void register(Format<T> format, Codec<T> codec) {
codecs.put(format, codec); codecs.put(format, codec);
} }
} }
@Override @Override
public String toString() { public String toString() {
return "DDTracer{" + return "DDTracer{" +
"writer=" + writer + "writer=" + writer +
", sampler=" + sampler + ", sampler=" + sampler +
'}'; '}';
} }
} }

View File

@ -2,7 +2,6 @@ package com.datadoghq.trace.integration;
import com.datadoghq.trace.DDSpanContext; import com.datadoghq.trace.DDSpanContext;
import com.datadoghq.trace.DDTags; import com.datadoghq.trace.DDTags;
import io.opentracing.tag.Tags; import io.opentracing.tag.Tags;
/** /**
@ -20,14 +19,12 @@ public class DBComponent extends DDSpanContextDecorator {
@Override @Override
public boolean afterSetTag(DDSpanContext context, String tag, Object value) { public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
//Assign service name //Assign service name
if(super.afterSetTag(context, tag, value)){ if (super.afterSetTag(context, tag, value)) {
//Assign span type to DB //Assign span type to DB
context.setSpanType("db"); context.setSpanType("db");
//Assign resource name //Assign resource name
if(tag.equals(Tags.DB_STATEMENT.getKey())){ context.setResourceName(String.valueOf(value));
context.setResourceName(String.valueOf(value));
}
return true; return true;
} }
return false; return false;

View File

@ -15,13 +15,13 @@ public abstract class DDSpanContextDecorator {
private String setValue; private String setValue;
public boolean afterSetTag(DDSpanContext context, String tag, Object value){ public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
if(tag.equals(this.getMatchingTag()) && (this.getMatchingValue()==null || value.equals(this.getMatchingValue()))){ if ((this.getMatchingValue() == null || value.equals(this.getMatchingValue()))) {
String targetTag = getSetTag()==null?tag:getSetTag(); String targetTag = getSetTag() == null ? tag : getSetTag();
String targetValue = getSetValue()==null?String.valueOf(value):getSetTag(); String targetValue = getSetValue() == null ? String.valueOf(value) : getSetTag();
context.setTag(targetTag, targetValue); context.setTag(targetTag, targetValue);
return true; return true;
}else{ } else {
return false; return false;
} }
} }

View File

@ -2,7 +2,6 @@ package com.datadoghq.trace.integration;
import com.datadoghq.trace.DDSpanContext; import com.datadoghq.trace.DDSpanContext;
import com.datadoghq.trace.DDTags; import com.datadoghq.trace.DDTags;
import io.opentracing.tag.Tags; import io.opentracing.tag.Tags;
@ -21,11 +20,12 @@ public class HTTPComponent extends DDSpanContextDecorator {
@Override @Override
public boolean afterSetTag(DDSpanContext context, String tag, Object value) { public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
//Assign service name //Assign service name
if(super.afterSetTag(context, tag, value)){ if (super.afterSetTag(context, tag, value)) {
//Assign span type to WEB //Assign span type to WEB
context.setSpanType("web"); context.setSpanType("web");
return true; return true;
}else{ } else {
return false; return false;
} }
} }

View File

@ -1,12 +1,11 @@
package com.datadoghq.trace.integration; package com.datadoghq.trace.integration;
import java.net.MalformedURLException;
import com.datadoghq.trace.DDSpanContext; import com.datadoghq.trace.DDSpanContext;
import com.datadoghq.trace.DDTags; import com.datadoghq.trace.DDTags;
import io.opentracing.tag.Tags; import io.opentracing.tag.Tags;
import java.net.MalformedURLException;
public class URLAsResourceName extends DDSpanContextDecorator { public class URLAsResourceName extends DDSpanContextDecorator {
public URLAsResourceName() { public URLAsResourceName() {
@ -18,17 +17,13 @@ public class URLAsResourceName extends DDSpanContextDecorator {
@Override @Override
public boolean afterSetTag(DDSpanContext context, String tag, Object value) { public boolean afterSetTag(DDSpanContext context, String tag, Object value) {
//Assign resource name //Assign resource name
if(tag.equals(Tags.HTTP_URL.getKey())){ try {
try { String path = new java.net.URL(String.valueOf(value)).getPath();
String path = new java.net.URL(String.valueOf(value)).getPath(); context.setTag(this.getSetTag(), path);
context.setTag(this.getSetTag(),path); } catch (MalformedURLException e) {
} catch (MalformedURLException e) { context.setResourceName(String.valueOf(value));
context.setResourceName(String.valueOf(value));
}
return true;
}else{
return false;
} }
return true;
} }
} }