diff --git a/src/main/java/com/datadoghq/trace/Codec.java b/src/main/java/com/datadoghq/trace/Codec.java new file mode 100644 index 0000000000..edd78a9ee3 --- /dev/null +++ b/src/main/java/com/datadoghq/trace/Codec.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, Uber Technologies, Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.datadoghq.trace; + + +import com.datadoghq.trace.impl.DDSpanContext; + +public interface Codec { + + void inject(DDSpanContext context, T carrier); + + DDSpanContext extract(T carrier); +} diff --git a/src/main/java/com/datadoghq/trace/impl/DDSpan.java b/src/main/java/com/datadoghq/trace/impl/DDSpan.java index 40346d7584..c086e5dfb9 100644 --- a/src/main/java/com/datadoghq/trace/impl/DDSpan.java +++ b/src/main/java/com/datadoghq/trace/impl/DDSpan.java @@ -118,7 +118,13 @@ public class DDSpan implements io.opentracing.Span { * @return true if root, false otherwise */ private boolean isRootSpan() { - return context.getTraceId() == context.getSpanId(); + + if (context().getTrace().isEmpty()) { + return false; + } + // First item of the array AND tracer set + DDSpan first = (DDSpan) context().getTrace().get(0); + return first.context.getSpanId() == this.context.getSpanId() && this.context.getTracer() != null; } /* (non-Javadoc) diff --git a/src/main/java/com/datadoghq/trace/impl/DDTracer.java b/src/main/java/com/datadoghq/trace/impl/DDTracer.java index 7ca83d7053..32006f9e5c 100644 --- a/src/main/java/com/datadoghq/trace/impl/DDTracer.java +++ b/src/main/java/com/datadoghq/trace/impl/DDTracer.java @@ -9,14 +9,17 @@ import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.datadoghq.trace.Codec; import com.datadoghq.trace.Sampler; import com.datadoghq.trace.Writer; +import com.datadoghq.trace.propagation.impl.HTTPCodec; import com.datadoghq.trace.writer.impl.LoggingWritter; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.propagation.Format; + /** * DDTracer makes it easy to send traces and span to DD using the OpenTracing instrumentation. */ @@ -30,7 +33,7 @@ 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; - + /** * Span context decorators */ @@ -38,6 +41,7 @@ public class DDTracer implements io.opentracing.Tracer { private final static Logger logger = LoggerFactory.getLogger(DDTracer.class); + private final CodecRegistry registry; /** * Default constructor, trace/spans are logged, no trace/span dropped @@ -49,21 +53,22 @@ public class DDTracer implements io.opentracing.Tracer { public DDTracer(Writer writer, Sampler sampler) { this.writer = writer; this.sampler = sampler; + registry = new CodecRegistry(); + registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec()); } - /** * Returns the list of span context decorators - * + * * @return the list of span context decorators */ public List getSpanContextDecorators() { return Collections.unmodifiableList(spanContextDecorators); } - + /** * Add a new decorator in the list ({@link DDSpanContextDecorator}) - * + * * @param decorator The decorator in the list */ public void addDecorator(DDSpanContextDecorator decorator){ @@ -74,14 +79,25 @@ public class DDTracer implements io.opentracing.Tracer { return new DDSpanBuilder(operationName); } - public void inject(SpanContext spanContext, Format format, C c) { - //FIXME Implement it ASAP - logger.warn("Method `inject` not implemented yet"); + + public void inject(SpanContext spanContext, Format format, T carrier) { + + Codec codec = registry.get(format); + if (codec == null) { + logger.warn("Unsupported format for propagation - {}", format.getClass().getName()); + } else { + codec.inject((DDSpanContext) spanContext, carrier); + } } - public SpanContext extract(Format format, C c) { - //FIXME Implement it ASAP - logger.warn("Method `inject` not implemented yet"); + public SpanContext extract(Format format, T carrier) { + + Codec codec = registry.get(format); + if (codec == null) { + logger.warn("Unsupported format for propagation - {}", format.getClass().getName()); + } else { + return codec.extract(carrier); + } return null; } @@ -263,6 +279,20 @@ public class DDTracer implements io.opentracing.Tracer { } + private static class CodecRegistry { + + private final Map, Codec> codecs = new HashMap, Codec>(); + + Codec get(Format format) { + return (Codec) codecs.get(format); + } + + public void register(Format format, Codec codec) { + codecs.put(format, codec); + } + + } + @Override public String toString() { return "DDTracer{" + diff --git a/src/main/java/com/datadoghq/trace/propagation/impl/HTTPCodec.java b/src/main/java/com/datadoghq/trace/propagation/impl/HTTPCodec.java new file mode 100644 index 0000000000..5a384bb678 --- /dev/null +++ b/src/main/java/com/datadoghq/trace/propagation/impl/HTTPCodec.java @@ -0,0 +1,104 @@ +package com.datadoghq.trace.propagation.impl; + +import com.datadoghq.trace.Codec; +import com.datadoghq.trace.impl.DDSpanContext; +import io.opentracing.propagation.TextMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + + +public class HTTPCodec implements Codec { + + private static final String OT_PREFIX = "ot-tracer-"; + private static final String OT_BAGGAGE_PREFIX = "ot-baggage-"; + private static final String TRACE_ID_KEY = OT_PREFIX + "traceid"; + private static final String SPAN_ID_KEY = OT_PREFIX + "spanid"; + + private static final Logger logger = LoggerFactory.getLogger(HTTPCodec.class); + + @Override + public void inject(DDSpanContext context, TextMap carrier) { + + carrier.put(TRACE_ID_KEY, String.valueOf(context.getTraceId())); + carrier.put(SPAN_ID_KEY, String.valueOf(context.getSpanId())); + + for (Map.Entry entry : context.baggageItems()) { + carrier.put(OT_BAGGAGE_PREFIX + entry.getKey(), encode(entry.getValue())); + } + } + + + + @Override + public DDSpanContext extract(TextMap carrier) { + + Map baggage = Collections.emptyMap(); + Long traceId = 0L; + Long spanId = 0L; + + for (Map.Entry entry : carrier) { + + if (entry.getKey().equals(TRACE_ID_KEY)) { + traceId = Long.parseLong(entry.getValue()); + } else if (entry.getKey().equals(SPAN_ID_KEY)) { + spanId = Long.parseLong(entry.getValue()); + } else if (entry.getKey().startsWith(OT_BAGGAGE_PREFIX)) { + if (baggage.isEmpty()) { + baggage = new HashMap(); + } + baggage.put(entry.getKey(), decode(entry.getValue())); + } + } + DDSpanContext context = null; + if (traceId != 0L) { + + context = new DDSpanContext( + traceId, + spanId, + 0L, + null, + null, + null, + baggage, + false, + null, + null, + null, + null); + + logger.debug("{} - Parent context extracted", context); + + } + + return context; + } + + + private String encode(String value) { + String encoded = value; + try { + encoded = URLEncoder.encode(value, "UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.info("Failed to encode value - {}", value); + } + return encoded; + } + + private String decode(String value) { + String decoded = value; + try { + decoded = URLDecoder.decode(value, "UTF-8"); + } catch (UnsupportedEncodingException e) { + logger.info("Failed to decode value - {}", value); + } + return decoded; + } + +} diff --git a/src/main/java/com/datadoghq/trace/writer/impl/DDApi.java b/src/main/java/com/datadoghq/trace/writer/impl/DDApi.java index 746449b63f..8e6ebce308 100644 --- a/src/main/java/com/datadoghq/trace/writer/impl/DDApi.java +++ b/src/main/java/com/datadoghq/trace/writer/impl/DDApi.java @@ -93,7 +93,7 @@ public class DDApi { out.write(content); out.close(); int responseCode = httpCon.getResponseCode(); - if (responseCode != 200) { + if (responseCode == 200) { logger.debug("Sent the payload to the DD agent."); } else { logger.warn("Could not send the payload to the DD agent. Status: {} ResponseMessage: {}", httpCon.getResponseCode(), httpCon.getResponseMessage());