From 832d8918c0d254982a4bc2ff46a66ee5d034075b Mon Sep 17 00:00:00 2001 From: renaudboutet Date: Fri, 12 May 2017 22:55:20 +0200 Subject: [PATCH] Added dd-trace.yaml config file resolution in resolver + default service name in tracer --- pom.xml | 157 +++++++++--------- .../java/com/datadoghq/trace/DDTracer.java | 31 +++- .../trace/integration/DBServiceDecorator.java | 8 + .../integration/HTTPServiceDecorator.java | 8 + .../trace/resolver/DDTracerResolver.java | 113 ++++++++++++- .../trace/resolver/TracerConfig.java | 39 +++++ .../datadoghq/trace/sampling/RateSampler.java | 6 +- .../datadoghq/trace/writer/DDAgentWriter.java | 18 +- .../trace/writer/LoggingWritter.java | 8 + .../com/datadoghq/trace/writer/Writer.java | 5 + .../java/com/datadoghq/trace/DDSpanTest.java | 4 +- .../trace/resolver/TracerResolverTest.java | 30 ++++ .../trace/writer/impl/DDAgentWriterTest.java | 1 + src/test/resources/dd-trace.yaml | 13 ++ 14 files changed, 352 insertions(+), 89 deletions(-) create mode 100644 src/main/java/com/datadoghq/trace/resolver/TracerConfig.java create mode 100644 src/test/java/com/datadoghq/trace/resolver/TracerResolverTest.java create mode 100644 src/test/resources/dd-trace.yaml diff --git a/pom.xml b/pom.xml index 920a3aee7a..7dbba38595 100644 --- a/pom.xml +++ b/pom.xml @@ -1,86 +1,91 @@ - 4.0.0 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 - com.datadog - raclette-java - 1.0-SNAPSHOT + com.datadog + raclette-java + 1.0-SNAPSHOT - - - io.opentracing - opentracing-api - 0.21.0 - - - com.fasterxml.jackson.core - jackson-databind - 2.8.8 - + + + io.opentracing + opentracing-api + 0.21.0 + + + com.fasterxml.jackson.core + jackson-databind + 2.8.8 + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + 2.8.8 + + + + + org.slf4j + slf4j-api + 1.7.25 + + + ch.qos.logback + logback-classic + 1.2.3 + + + net.logstash.logback + logstash-logback-encoder + 4.9 + - - - org.slf4j - slf4j-api - 1.7.25 - - - ch.qos.logback - logback-classic - 1.2.3 - - - net.logstash.logback - logstash-logback-encoder - 4.9 - + + + io.opentracing.contrib + opentracing-tracerresolver + 0.1.0 + - - - io.opentracing.contrib - opentracing-tracerresolver - 0.0.3 - + + com.google.auto.service + auto-service + 1.0-rc3 + - - com.google.auto.service - auto-service - 1.0-rc3 - + + + junit + junit + 4.12 + test + + + org.assertj + assertj-core + 3.6.2 + test + + + org.mockito + mockito-core + 2.7.22 + test + + - - - junit - junit - 4.12 - test - - - org.assertj - assertj-core - 3.6.2 - test - - - org.mockito - mockito-core - 2.7.22 - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.6 - 1.6 - - - - + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.6 + 1.6 + + + + \ No newline at end of file diff --git a/src/main/java/com/datadoghq/trace/DDTracer.java b/src/main/java/com/datadoghq/trace/DDTracer.java index 05ed067177..4c9ac49654 100644 --- a/src/main/java/com/datadoghq/trace/DDTracer.java +++ b/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; @@ -35,6 +35,11 @@ public class DDTracer implements io.opentracing.Tracer { * Sampler defines the sampling policy in order to reduce the number of traces for instance */ private final Sampler sampler; + + /** + * Default service name if none provided on the trace or span + */ + private final String defaultServiceName; /** * Span context decorators @@ -44,16 +49,30 @@ 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) { - serviceName = p.getServiceName(); + if (serviceName == null) { + if(p != null){ + serviceName = p.getServiceName(); + }else{ + serviceName = defaultServiceName; + } } //this.operationName, this.tags, diff --git a/src/main/java/com/datadoghq/trace/integration/DBServiceDecorator.java b/src/main/java/com/datadoghq/trace/integration/DBServiceDecorator.java index f2a2a9f4ff..ed6f82ff71 100644 --- a/src/main/java/com/datadoghq/trace/integration/DBServiceDecorator.java +++ b/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; + } } diff --git a/src/main/java/com/datadoghq/trace/integration/HTTPServiceDecorator.java b/src/main/java/com/datadoghq/trace/integration/HTTPServiceDecorator.java index a280c5a499..13aa983ff0 100644 --- a/src/main/java/com/datadoghq/trace/integration/HTTPServiceDecorator.java +++ b/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; + } } diff --git a/src/main/java/com/datadoghq/trace/resolver/DDTracerResolver.java b/src/main/java/com/datadoghq/trace/resolver/DDTracerResolver.java index 2b07114219..8cec205bdc 100644 --- a/src/main/java/com/datadoghq/trace/resolver/DDTracerResolver.java +++ b/src/main/java/com/datadoghq/trace/resolver/DDTracerResolver.java @@ -1,9 +1,30 @@ 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 WRITERS = ServiceLoader.load(Writer.class); +// private static final ServiceLoader SAMPLERS = ServiceLoader.load(Sampler.class); +// private static final ServiceLoader 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 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 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 RESOLVERS = ServiceLoader.load(TracerResolver.class); + + for (TracerResolver value : RESOLVERS){ + value.resolveTracer(); + } } } diff --git a/src/main/java/com/datadoghq/trace/resolver/TracerConfig.java b/src/main/java/com/datadoghq/trace/resolver/TracerConfig.java new file mode 100644 index 0000000000..a7c2bd7ce6 --- /dev/null +++ b/src/main/java/com/datadoghq/trace/resolver/TracerConfig.java @@ -0,0 +1,39 @@ +package com.datadoghq.trace.resolver; + +import java.util.List; +import java.util.Map; + +/** + * Tracer configuration + */ +public class TracerConfig { + private String defaultServiceName; + private Map writer; + private Map sampler; + private List> decorators; + + public String getDefaultServiceName() { + return defaultServiceName; + } + public void setDefaultServiceName(String defaultServiceName) { + this.defaultServiceName = defaultServiceName; + } + public Map getWriter() { + return writer; + } + public void setWriter(Map writer) { + this.writer = writer; + } + public Map getSampler() { + return sampler; + } + public void setSampler(Map sampler) { + this.sampler = sampler; + } + public List> getDecorators() { + return decorators; + } + public void setDecorators(List> decorators) { + this.decorators = decorators; + } +} diff --git a/src/main/java/com/datadoghq/trace/sampling/RateSampler.java b/src/main/java/com/datadoghq/trace/sampling/RateSampler.java index ce7908b7f0..3b34ddfaa0 100644 --- a/src/main/java/com/datadoghq/trace/sampling/RateSampler.java +++ b/src/main/java/com/datadoghq/trace/sampling/RateSampler.java @@ -1,10 +1,13 @@ 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 { diff --git a/src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java b/src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java index 7717097851..7758ffae31 100644 --- a/src/main/java/com/datadoghq/trace/writer/DDAgentWriter.java +++ b/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>(DEFAULT_MAX_SPANS); - - executor.submit(new SpansSendingTask()); - } /* (non-Javadoc) @@ -82,6 +82,14 @@ public class DDAgentWriter implements Writer { logger.warn("Cannot add a trace of {} as the async queue is full. Queue max size: {}", trace.size(), DEFAULT_MAX_SPANS); } } + + /* (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() diff --git a/src/main/java/com/datadoghq/trace/writer/LoggingWritter.java b/src/main/java/com/datadoghq/trace/writer/LoggingWritter.java index d53650470d..3bf99b098b 100644 --- a/src/main/java/com/datadoghq/trace/writer/LoggingWritter.java +++ b/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()"); + } } diff --git a/src/main/java/com/datadoghq/trace/writer/Writer.java b/src/main/java/com/datadoghq/trace/writer/Writer.java index 1644edbd0b..969954f8b1 100644 --- a/src/main/java/com/datadoghq/trace/writer/Writer.java +++ b/src/main/java/com/datadoghq/trace/writer/Writer.java @@ -15,6 +15,11 @@ public interface Writer { * @param trace the list of spans to write */ void write(List trace); + + /** + * Start the writer + */ + void start(); /** * Indicates to the writer that no future writing will come and it should terminates all connections and tasks diff --git a/src/test/java/com/datadoghq/trace/DDSpanTest.java b/src/test/java/com/datadoghq/trace/DDSpanTest.java index 301bcd1a83..78eb9a8f1a 100644 --- a/src/test/java/com/datadoghq/trace/DDSpanTest.java +++ b/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"); } diff --git a/src/test/java/com/datadoghq/trace/resolver/TracerResolverTest.java b/src/test/java/com/datadoghq/trace/resolver/TracerResolverTest.java new file mode 100644 index 0000000000..aa97c239cf --- /dev/null +++ b/src/test/java/com/datadoghq/trace/resolver/TracerResolverTest.java @@ -0,0 +1,30 @@ +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 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"); + } + +} diff --git a/src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java b/src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java index bf7722d9dd..f5da256de7 100644 --- a/src/test/java/com/datadoghq/trace/writer/impl/DDAgentWriterTest.java +++ b/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 diff --git a/src/test/resources/dd-trace.yaml b/src/test/resources/dd-trace.yaml new file mode 100644 index 0000000000..050cf94c09 --- /dev/null +++ b/src/test/resources/dd-trace.yaml @@ -0,0 +1,13 @@ +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