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 75d1d5820b..c3e1a34a28 100644 --- a/dd-trace/src/main/java/com/datadoghq/trace/DDTracer.java +++ b/dd-trace/src/main/java/com/datadoghq/trace/DDTracer.java @@ -7,13 +7,19 @@ import com.datadoghq.trace.sampling.AllSampler; import com.datadoghq.trace.sampling.Sampler; import com.datadoghq.trace.writer.LoggingWriter; import com.datadoghq.trace.writer.Writer; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.opentracing.ActiveSpan; import io.opentracing.ActiveSpanSource; import io.opentracing.BaseSpan; import io.opentracing.SpanContext; import io.opentracing.propagation.Format; import io.opentracing.util.ThreadLocalActiveSpanSource; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; import java.util.concurrent.ThreadLocalRandom; import lombok.extern.slf4j.Slf4j; @@ -37,6 +43,7 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac private final Map> spanContextDecorators = new HashMap<>(); private final CodecRegistry registry; + private final List services = new ArrayList<>(); /** Default constructor, trace/spans are logged, no trace/span dropped */ public DDTracer() { @@ -137,6 +144,33 @@ public class DDTracer extends ThreadLocalActiveSpanSource implements io.opentrac return "DDTracer{" + "writer=" + writer + ", sampler=" + sampler + '}'; } + /** + * Register additional information about a service. Service additional information are a Datadog + * feature only. Services are reported through a specific Datadog endpoint. + * + * @param service additional service information + */ + public void addServiceInfo(final Service service) { + services.add(service); + // Update the write + try { + // We don't bother to send multiple times the list of services at this time + writer.writeServices(services); + } catch (final Throwable ex) { + log.warn("Failed to report additional service information, reason: {}", ex.getMessage()); + } + } + + /** + * Return the list of additional service information registered + * + * @return the list of additional service information + */ + @JsonIgnore + public List getServiceInfo() { + return services; + } + private static class CodecRegistry { private final Map, Codec> codecs = new HashMap<>(); diff --git a/dd-trace/src/main/java/com/datadoghq/trace/Service.java b/dd-trace/src/main/java/com/datadoghq/trace/Service.java new file mode 100644 index 0000000000..1da519279f --- /dev/null +++ b/dd-trace/src/main/java/com/datadoghq/trace/Service.java @@ -0,0 +1,47 @@ +package com.datadoghq.trace; + +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Service { + + private final String name; + private final String appName; + private final Service.AppType appType; + + public Service(final String name, final String appName, final AppType appType) { + this.name = name; + this.appName = appName; + this.appType = appType; + } + + @JsonProperty("service") + public String getName() { + return name; + } + + @JsonProperty("app") + public String getAppName() { + return appName; + } + + @JsonProperty("app_type") + public AppType getAppType() { + return appType; + } + + public enum AppType { + WEB("web"), + DB("db"), + CUSTOM("custom"); + + private final String type; + + AppType(final String type) { + this.type = type; + } + + public String toString() { + return type; + } + } +} 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 3b27f6576c..ee506073ae 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,6 +1,7 @@ package com.datadoghq.trace.writer; import com.datadoghq.trace.DDBaseSpan; +import com.datadoghq.trace.Service; import com.google.auto.service.AutoService; import java.util.List; import java.util.concurrent.Callable; @@ -80,6 +81,9 @@ public class DDAgentWriter implements Writer { queueFullReported = false; } + @Override + public void writeServices(final List services) {} + /* (non-Javadoc) * @see com.datadoghq.trace.writer.Writer#start() */ 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 index 52debaa277..e6d2b27356 100644 --- a/dd-trace/src/main/java/com/datadoghq/trace/writer/ListWriter.java +++ b/dd-trace/src/main/java/com/datadoghq/trace/writer/ListWriter.java @@ -1,8 +1,10 @@ package com.datadoghq.trace.writer; import com.datadoghq.trace.DDBaseSpan; +import com.datadoghq.trace.Service; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; /** List writer used by tests mostly */ public class ListWriter extends CopyOnWriteArrayList>> implements Writer { @@ -20,6 +22,11 @@ public class ListWriter extends CopyOnWriteArrayList>> implem add(trace); } + @Override + public void writeServices(final List services) { + throw new NotImplementedException(); + } + @Override public void start() { 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 7579fc6e4f..e4a00c878e 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 @@ -1,6 +1,7 @@ package com.datadoghq.trace.writer; import com.datadoghq.trace.DDBaseSpan; +import com.datadoghq.trace.Service; import com.google.auto.service.AutoService; import java.util.List; import lombok.extern.slf4j.Slf4j; @@ -14,6 +15,11 @@ public class LoggingWriter implements Writer { log.info("write(trace): {}", trace); } + @Override + public void writeServices(final List services) { + log.info("additional service information: {}", services); + } + @Override public void close() { log.info("close()"); 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 3390b8bc91..c8244e530e 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 @@ -1,6 +1,7 @@ package com.datadoghq.trace.writer; import com.datadoghq.trace.DDBaseSpan; +import com.datadoghq.trace.Service; import java.util.List; /** A writer is responsible to send collected spans to some place */ @@ -13,6 +14,13 @@ public interface Writer { */ void write(List> trace); + /** + * Report additional service information to the endpoint + * + * @param services a list of extra information about services + */ + void writeServices(List services); + /** Start the writer */ void start(); diff --git a/dd-trace/src/test/groovy/com/datadoghq/trace/ServiceTest.groovy b/dd-trace/src/test/groovy/com/datadoghq/trace/ServiceTest.groovy new file mode 100644 index 0000000000..e00f893de8 --- /dev/null +++ b/dd-trace/src/test/groovy/com/datadoghq/trace/ServiceTest.groovy @@ -0,0 +1,63 @@ +package com.datadoghq.trace + +import com.datadoghq.trace.sampling.AllSampler +import com.datadoghq.trace.writer.DDAgentWriter +import org.mockito.Mockito +import spock.lang.Specification + +class ServiceTest extends Specification { + + + def "getter/setter"() { + + setup: + def service = new Service("service-name", "app-name", Service.AppType.CUSTOM) + + expect: + service.getName() == "service-name" + service.getAppName() == "app-name" + service.getAppType() == Service.AppType.CUSTOM + + } + + def "enum"() { + + expect: + Service.AppType.values().size() == 3 + Service.AppType.DB.toString() == "db" + Service.AppType.WEB.toString() == "web" + Service.AppType.CUSTOM.toString() == "custom" + + } + + def "add extra info about a specific service"() { + + setup: + def tracer = new DDTracer() + def service = new Service("service-name", "app-name", Service.AppType.CUSTOM) + + when: + tracer.addServiceInfo(service) + + then: + tracer.getServiceInfo().size() == 1 + tracer.getServiceInfo().get(0) == service + + } + + def "add a extra info is reported to the writer"() { + + setup: + def writer = Mockito.spy(new DDAgentWriter()) + def tracer = new DDTracer(writer, new AllSampler()) + + + when: + tracer.addServiceInfo(new Service("service-name", "app-name", Service.AppType.CUSTOM)) + + then: + Mockito.verify(writer, Mockito.times(1)).writeServices(Mockito.any(List.class)) + + } + +}