Added dd-trace.yaml config file resolution in resolver + default service name in tracer
MODIFY
pom.xml
pom.xml
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.dataformat</groupId>
+ <artifactId>jackson-dataformat-yaml</artifactId>
+ <version>2.8.8</version>
+ </dependency>
<!-- Logging dependencies -->
<dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-tracerresolver</artifactId>
- <version>0.0.3</version>
+ <version>0.1.0</version>
</dependency>
<dependency>

MODIFY
src/main/java/com/datadoghq/trace/DDTracer.java
src/main/java/com/datadoghq/trace/DDTracer.java
@@ -14,7 +14,7 @@ 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.LoggingWritter;
+import com.datadoghq.trace.writer.DDAgentWriter;
import com.datadoghq.trace.writer.Writer;
import io.opentracing.Span;
@@ -36,6 +36,11 @@ public class DDTracer implements io.opentracing.Tracer {
*/
private final Sampler sampler;
+ /**
+ * Default service name if none provided on the trace or span
+ */
+ private final String defaultServiceName;
+
/**
* Span context decorators
*/
@@ -45,15 +50,29 @@ public class DDTracer implements io.opentracing.Tracer {
private final static Logger logger = LoggerFactory.getLogger(DDTracer.class);
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 Sampler UNASSIGNED_SAMPLER = new AllSampler();
+
/**
* Default constructor, trace/spans are logged, no trace/span dropped
*/
public DDTracer() {
- this(new LoggingWritter(), new AllSampler());
+ this(UNASSIGNED_WRITER);
+ }
+
+ public DDTracer(Writer writer) {
+ this(writer, new AllSampler());
}
public DDTracer(Writer writer, Sampler sampler) {
+ this(UNASSIGNED_DEFAULT_SERVICE_NAME,writer,sampler);
+ }
+
+ public DDTracer(String defaultServiceName,Writer writer, Sampler sampler) {
+ this.defaultServiceName = defaultServiceName;
this.writer = writer;
+ this.writer.start();
this.sampler = sampler;
registry = new CodecRegistry();
registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec());
@@ -261,8 +280,12 @@ public class DDTracer implements io.opentracing.Tracer {
}
String serviceName = this.serviceName;
- if (serviceName == null && this.parent != null) {
+ if (serviceName == null) {
+ if(p != null){
serviceName = p.getServiceName();
+ }else{
+ serviceName = defaultServiceName;
+ }
}
//this.operationName, this.tags,

MODIFY
src/main/java/com/datadoghq/trace/integration/DBServiceDecorator.java
src/main/java/com/datadoghq/trace/integration/DBServiceDecorator.java
@@ -40,4 +40,12 @@ public class DBServiceDecorator implements DDSpanContextDecorator {
context.setResourceName(String.valueOf(value));
}
}
+
+ public String getComponentName() {
+ return componentName;
+ }
+
+ public String getDesiredServiceName() {
+ return desiredServiceName;
+ }
}

MODIFY
src/main/java/com/datadoghq/trace/integration/HTTPServiceDecorator.java
src/main/java/com/datadoghq/trace/integration/HTTPServiceDecorator.java
@@ -48,4 +48,12 @@ public class HTTPServiceDecorator implements DDSpanContextDecorator {
}
}
}
+
+ public String getComponentName() {
+ return componentName;
+ }
+
+ public String getDesiredServiceName() {
+ return desiredServiceName;
+ }
}

MODIFY
src/main/java/com/datadoghq/trace/resolver/DDTracerResolver.java
src/main/java/com/datadoghq/trace/resolver/DDTracerResolver.java
package com.datadoghq.trace.resolver;
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
import com.datadoghq.trace.DDTracer;
+import com.datadoghq.trace.integration.DBServiceDecorator;
+import com.datadoghq.trace.integration.DDSpanContextDecorator;
+import com.datadoghq.trace.integration.HTTPServiceDecorator;
import com.datadoghq.trace.sampling.AllSampler;
+import com.datadoghq.trace.sampling.RateSampler;
+import com.datadoghq.trace.sampling.Sampler;
import com.datadoghq.trace.writer.DDAgentWriter;
+import com.datadoghq.trace.writer.DDApi;
+import com.datadoghq.trace.writer.LoggingWritter;
+import com.datadoghq.trace.writer.Writer;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.google.auto.service.AutoService;
+
import io.opentracing.Tracer;
import io.opentracing.contrib.tracerresolver.TracerResolver;
@@ -11,9 +32,97 @@ import io.opentracing.contrib.tracerresolver.TracerResolver;
@AutoService(TracerResolver.class)
public class DDTracerResolver extends TracerResolver{
+ private final static Logger logger = LoggerFactory.getLogger(DDTracerResolver.class);
+
+// private static final ServiceLoader<Writer> WRITERS = ServiceLoader.load(Writer.class);
+// private static final ServiceLoader<Sampler> SAMPLERS = ServiceLoader.load(Sampler.class);
+// private static final ServiceLoader<DDSpanContextDecorator> DECORATORS = ServiceLoader.load(DDSpanContextDecorator.class);
+
+ public static final String TRACER_CONFIG = "dd-trace.yaml";
+ private final ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
+
@Override
protected Tracer resolve() {
- // TODO find a way to close the reader ...
- return new DDTracer(new DDAgentWriter(), new AllSampler());
+ logger.info("Creating the Datadog tracer");
+
+ //Find a resource file named dd-trace.yml
+ DDTracer tracer = null;
+ try {
+ Enumeration<URL> iter = Thread.currentThread().getContextClassLoader().getResources(TRACER_CONFIG);
+ while (iter.hasMoreElements()) {
+
+ TracerConfig config = objectMapper.readValue(new File(iter.nextElement().getFile()),TracerConfig.class);
+
+ String defaultServiceName = config.getDefaultServiceName()!=null?config.getDefaultServiceName():DDTracer.UNASSIGNED_DEFAULT_SERVICE_NAME;
+
+ //Create writer
+ Writer writer = DDTracer.UNASSIGNED_WRITER;
+ if(config.getWriter()!=null &&config.getWriter().get("type")!=null){
+ String type = (String) config.getWriter().get("type");
+ if(type.equals(DDAgentWriter.class.getSimpleName())){
+ String host = config.getWriter().get("host")!=null?(String)config.getWriter().get("host"):DDAgentWriter.DEFAULT_HOSTNAME;
+ Integer port = config.getWriter().get("port")!=null?(Integer)config.getWriter().get("port"):DDAgentWriter.DEFAULT_PORT;
+ DDApi api = new DDApi(host, port);
+ writer = new DDAgentWriter(api);
+ }else if(type.equals(LoggingWritter.class.getSimpleName())){
+ writer = new LoggingWritter();
+ }
+ }
+
+ //Create sampler
+ Sampler rateSampler = DDTracer.UNASSIGNED_SAMPLER;
+ if(config.getSampler()!=null && config.getSampler().get("type")!=null){
+ String type = (String) config.getSampler().get("type");
+ if(type.equals(AllSampler.class.getSimpleName())){
+ rateSampler = new AllSampler();
+ }else if(type.equals(RateSampler.class.getSimpleName())){
+ rateSampler = new RateSampler((Double)config.getSampler().get("rate"));
+ }
+ }
+
+ //Create tracer
+ tracer = new DDTracer(defaultServiceName,writer,rateSampler);
+
+ //Find decorators
+ if(config.getDecorators()!=null){
+ for(Map<String,Object> map : config.getDecorators()){
+ if(map.get("type")!=null){
+ DDSpanContextDecorator decorator = null;
+ String componentName = (String) map.get("componentName");
+ String desiredServiceName = map.get("desiredServiceName")!=null?(String) map.get("desiredServiceName"):(String) map.get("componentName");
+
+ if(map.get("type").equals(HTTPServiceDecorator.class.getSimpleName())){
+ decorator = new HTTPServiceDecorator(componentName,desiredServiceName);
+ tracer.addDecorator(decorator);
+ }else if(map.get("type").equals(DBServiceDecorator.class.getSimpleName())){
+ decorator = new DBServiceDecorator(componentName,desiredServiceName);
+ tracer.addDecorator(decorator);
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ } catch (IOException e) {
+ logger.error("Could not load tracer configuration file. Loading default tracer.",e);
+ }
+
+ if(tracer==null){
+ logger.info("No valid configuration file 'dd-trace.yaml' found. Loading default tracer.");
+ tracer = new DDTracer();
+ }
+
+ return tracer;
+ }
+
+
+
+ public static void main(String[] args){
+ ServiceLoader<TracerResolver> RESOLVERS = ServiceLoader.load(TracerResolver.class);
+
+ for (TracerResolver value : RESOLVERS){
+ value.resolveTracer();
+ }
}
}

ADD
src/main/java/com/datadoghq/trace/resolver/TracerConfig.java
+package com.datadoghq.trace.resolver;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tracer configuration
+ */
+public class TracerConfig {
+ private String defaultServiceName;
+ private Map<String,Object> writer;
+ private Map<String,Object> sampler;
+ private List<Map<String,Object>> decorators;
+
+ public String getDefaultServiceName() {
+ return defaultServiceName;
+ }
+ public void setDefaultServiceName(String defaultServiceName) {
+ this.defaultServiceName = defaultServiceName;
+ }
+ public Map<String, Object> getWriter() {
+ return writer;
+ }
+ public void setWriter(Map<String, Object> writer) {
+ this.writer = writer;
+ }
+ public Map<String, Object> getSampler() {
+ return sampler;
+ }
+ public void setSampler(Map<String, Object> sampler) {
+ this.sampler = sampler;
+ }
+ public List<Map<String, Object>> getDecorators() {
+ return decorators;
+ }
+ public void setDecorators(List<Map<String, Object>> decorators) {
+ this.decorators = decorators;
+ }
+}

MODIFY
src/main/java/com/datadoghq/trace/sampling/RateSampler.java
src/main/java/com/datadoghq/trace/sampling/RateSampler.java
package com.datadoghq.trace.sampling;
-import io.opentracing.Span;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
+import io.opentracing.Span;
+
/**
* This sampler sample the traces at a predefined rate.
@@ -12,6 +15,7 @@ import org.slf4j.LoggerFactory;
* Keep (100 * `sample_rate`)% of the traces.
* It samples randomly, its main purpose is to reduce the instrumentation footprint.
*/
+@AutoService(Sampler.class)
public class RateSampler implements Sampler {

MODIFY
src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java
src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java
@@ -4,6 +4,8 @@ import io.opentracing.Span;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
@@ -14,6 +16,7 @@ import java.util.concurrent.*;
* It handles writes asynchronuously so the calling threads are automatically released. However, if too much spans are collected
* the writers can reach a state where it is forced to drop incoming spans.
*/
+@AutoService(Writer.class)
public class DDAgentWriter implements Writer {
private static final Logger logger = LoggerFactory.getLogger(DDAgentWriter.class.getName());
@@ -21,8 +24,8 @@ public class DDAgentWriter implements Writer {
/**
* Default location of the DD agent
*/
- private static final String DEFAULT_HOSTNAME = "localhost";
- private static final int DEFAULT_PORT = 8126;
+ public static final String DEFAULT_HOSTNAME = "localhost";
+ public static final int DEFAULT_PORT = 8126;
/**
* Maximum number of spans kept in memory
@@ -64,9 +67,6 @@ public class DDAgentWriter implements Writer {
tokens = new Semaphore(DEFAULT_MAX_SPANS);
traces = new ArrayBlockingQueue<List<Span>>(DEFAULT_MAX_SPANS);
-
- executor.submit(new SpansSendingTask());
-
}
/* (non-Javadoc)
@@ -83,6 +83,14 @@ public class DDAgentWriter implements Writer {
}
}
+ /* (non-Javadoc)
+ * @see com.datadoghq.trace.writer.Writer#start()
+ */
+ @Override
+ public void start() {
+ executor.submit(new SpansSendingTask());
+ }
+
/* (non-Javadoc)
* @see com.datadoghq.trace.Writer#close()
*/

MODIFY
src/main/java/com/datadoghq/trace/writer/LoggingWritter.java
src/main/java/com/datadoghq/trace/writer/LoggingWritter.java
@@ -5,8 +5,11 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.auto.service.AutoService;
+
import io.opentracing.Span;
+@AutoService(Writer.class)
public class LoggingWritter implements Writer{
private static final Logger logger = LoggerFactory.getLogger(LoggingWritter.class.getName());
@@ -20,4 +23,9 @@ public class LoggingWritter implements Writer{
public void close() {
logger.info("close()");
}
+
+ @Override
+ public void start() {
+ logger.info("start()");
+ }
}

MODIFY
src/main/java/com/datadoghq/trace/writer/Writer.java
src/main/java/com/datadoghq/trace/writer/Writer.java
@@ -16,6 +16,11 @@ public interface Writer {
*/
void write(List<Span> trace);
+ /**
+ * Start the writer
+ */
+ void start();
+
/**
* Indicates to the writer that no future writing will come and it should terminates all connections and tasks
*/

MODIFY
src/test/java/com/datadoghq/trace/DDSpanTest.java
src/test/java/com/datadoghq/trace/DDSpanTest.java
@@ -59,9 +59,10 @@ public class DDSpanTest {
final String expectedName = "operationName";
- DDSpan span = new DDTracer().buildSpan(expectedName).withServiceName("foo").start();
+ DDSpan span = new DDTracer().buildSpan(expectedName).start();
// ResourceName = expectedName
assertThat(span.getResourceName()).isEqualTo(expectedName);
+ assertThat(span.getServiceName()).isEqualTo(DDTracer.UNASSIGNED_DEFAULT_SERVICE_NAME);
// ResourceName = expectedResourceName
final String expectedResourceName = "fake";
@@ -71,6 +72,7 @@ public class DDSpanTest {
.withServiceName("foo").start();
assertThat(span.getResourceName()).isEqualTo(expectedResourceName);
+ assertThat(span.getServiceName()).isEqualTo("foo");
}

ADD
src/test/java/com/datadoghq/trace/resolver/TracerResolverTest.java
+package com.datadoghq.trace.resolver;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.List;
+
+import org.junit.Test;
+
+import com.datadoghq.trace.DDTracer;
+import com.datadoghq.trace.integration.DDSpanContextDecorator;
+import com.datadoghq.trace.integration.HTTPServiceDecorator;
+
+public class TracerResolverTest {
+
+ @Test
+ public void test() {
+ DDTracerResolver tracerResolver = new DDTracerResolver();
+ DDTracer tracer = (DDTracer) tracerResolver.resolve();
+
+ List<DDSpanContextDecorator> decorators = tracer.getSpanContextDecorators();
+
+ assertThat(decorators.size()).isEqualTo(1);
+ DDSpanContextDecorator decorator = decorators.get(0);
+ assertThat(decorator.getClass()).isEqualTo(HTTPServiceDecorator.class);
+ HTTPServiceDecorator httpServiceDecorator = (HTTPServiceDecorator) decorator;
+ assertThat(httpServiceDecorator.getComponentName()).isEqualTo("hello");
+ assertThat(httpServiceDecorator.getDesiredServiceName()).isEqualTo("world");
+ }
+
+}

MODIFY
src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java
src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java
@@ -47,6 +47,7 @@ public class DDAgentWriterTest {
mockedAPI = mock(DDApi.class);
when(mockedAPI.sendTraces(traces)).thenReturn(true);
ddAgentWriter = new DDAgentWriter(mockedAPI);
+ ddAgentWriter.start();
}
@Test

ADD
src/test/resources/dd-trace.yaml
+defaultServiceName: java-app
+writer:
+ type: DDAgentWriter
+ host: localhost
+ port: 10000
+sampler:
+ type: AllSampler
+decorators:
+ - type: HTTPServiceDecorator
+ componentName: hello
+ desiredServiceName: world
+
+
\ No newline at end of file

Added dd-trace.yaml config file resolution in resolver + default service name in tracer