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

This commit is contained in:
renaudboutet 2017-05-12 22:55:20 +02:00
parent fa98552f82
commit 832d8918c0
14 changed files with 352 additions and 89 deletions

157
pom.xml
View File

@ -1,86 +1,91 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.datadog</groupId>
<artifactId>raclette-java</artifactId>
<version>1.0-SNAPSHOT</version>
<groupId>com.datadog</groupId>
<artifactId>raclette-java</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>0.21.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</dependency>
<dependencies>
<dependency>
<groupId>io.opentracing</groupId>
<artifactId>opentracing-api</artifactId>
<version>0.21.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<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>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.9</version>
</dependency>
<!-- Logging dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>4.9</version>
</dependency>
<!-- Tracer resolver -->
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-tracerresolver</artifactId>
<version>0.1.0</version>
</dependency>
<!-- Tracer resolver -->
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-tracerresolver</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc3</version>
</dependency>
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.0-rc3</version>
</dependency>
<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.7.22</version>
<scope>test</scope>
</dependency>
</dependencies>
<!-- Testing dependencies -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.7.22</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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,

View File

@ -40,4 +40,12 @@ public class DBServiceDecorator implements DDSpanContextDecorator {
context.setResourceName(String.valueOf(value));
}
}
public String getComponentName() {
return componentName;
}
public String getDesiredServiceName() {
return desiredServiceName;
}
}

View File

@ -48,4 +48,12 @@ public class HTTPServiceDecorator implements DDSpanContextDecorator {
}
}
}
public String getComponentName() {
return componentName;
}
public String getDesiredServiceName() {
return desiredServiceName;
}
}

View File

@ -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<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();
}
}
}

View File

@ -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<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;
}
}

View File

@ -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 {

View File

@ -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)
@ -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()

View File

@ -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()");
}
}

View File

@ -15,6 +15,11 @@ public interface Writer {
* @param trace the list of spans to write
*/
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

View File

@ -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");
}

View File

@ -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<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");
}
}

View File

@ -47,6 +47,7 @@ public class DDAgentWriterTest {
mockedAPI = mock(DDApi.class);
when(mockedAPI.sendTraces(traces)).thenReturn(true);
ddAgentWriter = new DDAgentWriter(mockedAPI);
ddAgentWriter.start();
}
@Test

View File

@ -0,0 +1,13 @@
defaultServiceName: java-app
writer:
type: DDAgentWriter
host: localhost
port: 10000
sampler:
type: AllSampler
decorators:
- type: HTTPServiceDecorator
componentName: hello
desiredServiceName: world