Merge pull request #317 from DataDog/tyler/tagged-headers

Add setting for collecting headers as tags during extraction
This commit is contained in:
Tyler Benson 2018-05-15 10:43:54 +10:00 committed by GitHub
commit 69ee39bfc2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 71 additions and 9 deletions

View File

@ -88,7 +88,8 @@ public class DDTracer implements io.opentracing.Tracer {
Writer.Builder.forConfig(config), Writer.Builder.forConfig(config),
Sampler.Builder.forConfig(config), Sampler.Builder.forConfig(config),
DDTraceConfig.parseMap(config.getProperty(DDTraceConfig.SPAN_TAGS)), DDTraceConfig.parseMap(config.getProperty(DDTraceConfig.SPAN_TAGS)),
DDTraceConfig.parseMap(config.getProperty(DDTraceConfig.SERVICE_MAPPING))); DDTraceConfig.parseMap(config.getProperty(DDTraceConfig.SERVICE_MAPPING)),
DDTraceConfig.parseMap(config.getProperty(DDTraceConfig.HEADER_TAGS)));
log.debug("Using config: {}", config); log.debug("Using config: {}", config);
} }
@ -98,6 +99,7 @@ public class DDTracer implements io.opentracing.Tracer {
writer, writer,
sampler, sampler,
Collections.<String, String>emptyMap(), Collections.<String, String>emptyMap(),
Collections.<String, String>emptyMap(),
Collections.<String, String>emptyMap()); Collections.<String, String>emptyMap());
} }
@ -106,7 +108,8 @@ public class DDTracer implements io.opentracing.Tracer {
final Writer writer, final Writer writer,
final Sampler sampler, final Sampler sampler,
final Map<String, String> defaultSpanTags, final Map<String, String> defaultSpanTags,
final Map<String, String> serviceNameMappings) { final Map<String, String> serviceNameMappings,
final Map<String, String> taggedHeaders) {
this.serviceName = serviceName; this.serviceName = serviceName;
this.writer = writer; this.writer = writer;
this.writer.start(); this.writer.start();
@ -123,8 +126,8 @@ public class DDTracer implements io.opentracing.Tracer {
}); });
registry = new CodecRegistry(); registry = new CodecRegistry();
registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec()); registry.register(Format.Builtin.HTTP_HEADERS, new HTTPCodec(taggedHeaders));
registry.register(Format.Builtin.TEXT_MAP, new HTTPCodec()); registry.register(Format.Builtin.TEXT_MAP, new HTTPCodec(taggedHeaders));
if (this.writer instanceof DDAgentWriter && sampler instanceof DDApi.ResponseListener) { if (this.writer instanceof DDAgentWriter && sampler instanceof DDApi.ResponseListener) {
final DDApi api = ((DDAgentWriter) this.writer).getApi(); final DDApi api = ((DDAgentWriter) this.writer).getApi();
api.addResponseListener((DDApi.ResponseListener) this.sampler); api.addResponseListener((DDApi.ResponseListener) this.sampler);
@ -149,7 +152,8 @@ public class DDTracer implements io.opentracing.Tracer {
writer, writer,
new AllSampler(), new AllSampler(),
DDTraceConfig.parseMap(new DDTraceConfig().getProperty(DDTraceConfig.SPAN_TAGS)), DDTraceConfig.parseMap(new DDTraceConfig().getProperty(DDTraceConfig.SPAN_TAGS)),
DDTraceConfig.parseMap(new DDTraceConfig().getProperty(DDTraceConfig.SERVICE_MAPPING))); DDTraceConfig.parseMap(new DDTraceConfig().getProperty(DDTraceConfig.SERVICE_MAPPING)),
DDTraceConfig.parseMap(new DDTraceConfig().getProperty(DDTraceConfig.HEADER_TAGS)));
} }
/** /**
@ -489,6 +493,12 @@ public class DDTracer implements io.opentracing.Tracer {
traceId = ddsc.getTraceId(); traceId = ddsc.getTraceId();
parentSpanId = ddsc.getSpanId(); parentSpanId = ddsc.getSpanId();
baggage = ddsc.getBaggage(); baggage = ddsc.getBaggage();
if (this.tags.isEmpty() && !ddsc.getTags().isEmpty()) {
this.tags = new HashMap<>();
}
if (!ddsc.getTags().isEmpty()) {
tags.putAll(ddsc.getTags());
}
parentTrace = new PendingTrace(DDTracer.this, traceId); parentTrace = new PendingTrace(DDTracer.this, traceId);
samplingPriority = ddsc.getSamplingPriority(); samplingPriority = ddsc.getSamplingPriority();

View File

@ -9,17 +9,20 @@ public class ExtractedContext implements SpanContext {
private final Long spanId; private final Long spanId;
private final int samplingPriority; private final int samplingPriority;
private final Map<String, String> baggage; private final Map<String, String> baggage;
private final Map<String, String> tags;
private final AtomicBoolean samplingPriorityLocked = new AtomicBoolean(false); private final AtomicBoolean samplingPriorityLocked = new AtomicBoolean(false);
public ExtractedContext( public ExtractedContext(
final Long traceId, final Long traceId,
final Long spanId, final Long spanId,
final int samplingPriority, final int samplingPriority,
final Map<String, String> baggage) { final Map<String, String> baggage,
final Map<String, String> tags) {
this.traceId = traceId; this.traceId = traceId;
this.spanId = spanId; this.spanId = spanId;
this.samplingPriority = samplingPriority; this.samplingPriority = samplingPriority;
this.baggage = baggage; this.baggage = baggage;
this.tags = tags;
} }
@Override @Override
@ -47,6 +50,10 @@ public class ExtractedContext implements SpanContext {
return baggage; return baggage;
} }
public Map<String, String> getTags() {
return tags;
}
public boolean getSamplingPriorityLocked() { public boolean getSamplingPriorityLocked() {
return samplingPriorityLocked.get(); return samplingPriorityLocked.get();
} }

View File

@ -20,6 +20,15 @@ public class HTTPCodec implements Codec<TextMap> {
private static final String SPAN_ID_KEY = "x-datadog-parent-id"; private static final String SPAN_ID_KEY = "x-datadog-parent-id";
private static final String SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority"; private static final String SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority";
private final Map<String, String> taggedHeaders;
public HTTPCodec(final Map<String, String> taggedHeaders) {
this.taggedHeaders = new HashMap<>();
for (final Map.Entry<String, String> mapping : taggedHeaders.entrySet()) {
this.taggedHeaders.put(mapping.getKey().trim().toLowerCase(), mapping.getValue());
}
}
@Override @Override
public void inject(final DDSpanContext context, final TextMap carrier) { public void inject(final DDSpanContext context, final TextMap carrier) {
carrier.put(TRACE_ID_KEY, String.valueOf(context.getTraceId())); carrier.put(TRACE_ID_KEY, String.valueOf(context.getTraceId()));
@ -38,6 +47,7 @@ public class HTTPCodec implements Codec<TextMap> {
public ExtractedContext extract(final TextMap carrier) { public ExtractedContext extract(final TextMap carrier) {
Map<String, String> baggage = Collections.emptyMap(); Map<String, String> baggage = Collections.emptyMap();
Map<String, String> tags = Collections.emptyMap();
Long traceId = 0L; Long traceId = 0L;
Long spanId = 0L; Long spanId = 0L;
int samplingPriority = PrioritySampling.UNSET; int samplingPriority = PrioritySampling.UNSET;
@ -56,10 +66,17 @@ public class HTTPCodec implements Codec<TextMap> {
} else if (key.equalsIgnoreCase(SAMPLING_PRIORITY_KEY)) { } else if (key.equalsIgnoreCase(SAMPLING_PRIORITY_KEY)) {
samplingPriority = Integer.parseInt(entry.getValue()); samplingPriority = Integer.parseInt(entry.getValue());
} }
if (taggedHeaders.containsKey(key)) {
if (tags.isEmpty()) {
tags = new HashMap<>();
}
tags.put(taggedHeaders.get(key), decode(entry.getValue()));
}
} }
ExtractedContext context = null; ExtractedContext context = null;
if (traceId != 0L) { if (traceId != 0L) {
context = new ExtractedContext(traceId, spanId, samplingPriority, baggage); context = new ExtractedContext(traceId, spanId, samplingPriority, baggage, tags);
context.lockSamplingPriority(); context.lockSamplingPriority();
log.debug("{} - Parent context extracted", context.getTraceId()); log.debug("{} - Parent context extracted", context.getTraceId());

View File

@ -30,6 +30,7 @@ public class DDTraceConfig extends Properties {
public static final String AGENT_PORT = "agent.port"; public static final String AGENT_PORT = "agent.port";
public static final String PRIORITY_SAMPLING = "priority.sampling"; public static final String PRIORITY_SAMPLING = "priority.sampling";
public static final String SPAN_TAGS = "trace.span.tags"; public static final String SPAN_TAGS = "trace.span.tags";
public static final String HEADER_TAGS = "trace.header.tags";
private final String serviceName = getPropOrEnv(PREFIX + SERVICE_NAME); private final String serviceName = getPropOrEnv(PREFIX + SERVICE_NAME);
private final String serviceMapping = getPropOrEnv(PREFIX + SERVICE_MAPPING); private final String serviceMapping = getPropOrEnv(PREFIX + SERVICE_MAPPING);
@ -38,6 +39,7 @@ public class DDTraceConfig extends Properties {
private final String agentPort = getPropOrEnv(PREFIX + AGENT_PORT); private final String agentPort = getPropOrEnv(PREFIX + AGENT_PORT);
private final String prioritySampling = getPropOrEnv(PREFIX + PRIORITY_SAMPLING); private final String prioritySampling = getPropOrEnv(PREFIX + PRIORITY_SAMPLING);
private final String spanTags = getPropOrEnv(PREFIX + SPAN_TAGS); private final String spanTags = getPropOrEnv(PREFIX + SPAN_TAGS);
private final String headerTags = getPropOrEnv(PREFIX + HEADER_TAGS);
public DDTraceConfig() { public DDTraceConfig() {
super(); super();
@ -56,6 +58,7 @@ public class DDTraceConfig extends Properties {
setIfNotNull(AGENT_PORT, agentPort); setIfNotNull(AGENT_PORT, agentPort);
setIfNotNull(PRIORITY_SAMPLING, prioritySampling); setIfNotNull(PRIORITY_SAMPLING, prioritySampling);
setIfNotNull(SPAN_TAGS, spanTags); setIfNotNull(SPAN_TAGS, spanTags);
setIfNotNull(HEADER_TAGS, headerTags);
} }
public DDTraceConfig(final String serviceName) { public DDTraceConfig(final String serviceName) {

View File

@ -1,5 +1,6 @@
package datadog.opentracing package datadog.opentracing
import datadog.opentracing.propagation.ExtractedContext
import datadog.trace.api.DDTags import datadog.trace.api.DDTags
import datadog.trace.common.writer.ListWriter import datadog.trace.common.writer.ListWriter
import spock.lang.Specification import spock.lang.Specification
@ -222,6 +223,24 @@ class DDSpanBuilderTest extends Specification {
spans[(int) (Math.random() * nbSamples)].context.trace.containsAll(spans) spans[(int) (Math.random() * nbSamples)].context.trace.containsAll(spans)
} }
def "ExtractedContext should populate new span details"() {
setup:
final DDSpan span = tracer.buildSpan("op name")
.asChildOf(extractedContext).start()
expect:
span.traceId == extractedContext.traceId
span.parentId == extractedContext.spanId
span.samplingPriority == extractedContext.samplingPriority
span.context().baggageItems == extractedContext.baggage
span.context().@tags == extractedContext.tags
where:
extractedContext | _
new ExtractedContext(1, 2, 0, [:], [:]) | _
new ExtractedContext(3, 4, 1, ["asdf": "qwer"], ["zxcv": "1234"]) | _
}
def "global span tags populated on each span"() { def "global span tags populated on each span"() {
setup: setup:
System.setProperty("dd.trace.span.tags", tagString) System.setProperty("dd.trace.span.tags", tagString)

View File

@ -20,6 +20,8 @@ class HTTPCodecTest extends Specification {
@Shared @Shared
private static final String SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority" private static final String SAMPLING_PRIORITY_KEY = "x-datadog-sampling-priority"
HTTPCodec codec = new HTTPCodec(["SOME_HEADER": "some-tag"])
def "inject http headers"() { def "inject http headers"() {
setup: setup:
def writer = new ListWriter() def writer = new ListWriter()
@ -47,7 +49,6 @@ class HTTPCodecTest extends Specification {
final Map<String, String> carrier = new HashMap<>() final Map<String, String> carrier = new HashMap<>()
final HTTPCodec codec = new HTTPCodec()
codec.inject(mockedContext, new TextMapInjectAdapter(carrier)) codec.inject(mockedContext, new TextMapInjectAdapter(carrier))
expect: expect:
@ -70,13 +71,13 @@ class HTTPCodecTest extends Specification {
(SPAN_ID_KEY.toUpperCase()) : "2", (SPAN_ID_KEY.toUpperCase()) : "2",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k1"): "v1", (OT_BAGGAGE_PREFIX.toUpperCase() + "k1"): "v1",
(OT_BAGGAGE_PREFIX.toUpperCase() + "k2"): "v2", (OT_BAGGAGE_PREFIX.toUpperCase() + "k2"): "v2",
SOME_HEADER : "my-interesting-info",
] ]
if (samplingPriority != PrioritySampling.UNSET) { if (samplingPriority != PrioritySampling.UNSET) {
actual.put(SAMPLING_PRIORITY_KEY, String.valueOf(samplingPriority)) actual.put(SAMPLING_PRIORITY_KEY, String.valueOf(samplingPriority))
} }
final HTTPCodec codec = new HTTPCodec()
final ExtractedContext context = codec.extract(new TextMapExtractAdapter(actual)) final ExtractedContext context = codec.extract(new TextMapExtractAdapter(actual))
expect: expect:
@ -84,6 +85,7 @@ class HTTPCodecTest extends Specification {
context.getSpanId() == 2l context.getSpanId() == 2l
context.getBaggage().get("k1") == "v1" context.getBaggage().get("k1") == "v1"
context.getBaggage().get("k2") == "v2" context.getBaggage().get("k2") == "v2"
context.getTags() == ["some-tag": "my-interesting-info"]
context.getSamplingPriority() == samplingPriority context.getSamplingPriority() == samplingPriority
where: where:

View File

@ -14,6 +14,7 @@ import spock.lang.Specification
import static datadog.trace.common.DDTraceConfig.AGENT_HOST import static datadog.trace.common.DDTraceConfig.AGENT_HOST
import static datadog.trace.common.DDTraceConfig.AGENT_PORT import static datadog.trace.common.DDTraceConfig.AGENT_PORT
import static datadog.trace.common.DDTraceConfig.HEADER_TAGS
import static datadog.trace.common.DDTraceConfig.PREFIX import static datadog.trace.common.DDTraceConfig.PREFIX
import static datadog.trace.common.DDTraceConfig.SERVICE_MAPPING import static datadog.trace.common.DDTraceConfig.SERVICE_MAPPING
import static datadog.trace.common.DDTraceConfig.SERVICE_NAME import static datadog.trace.common.DDTraceConfig.SERVICE_NAME
@ -114,16 +115,19 @@ class DDTraceConfigTest extends Specification {
setup: setup:
System.setProperty(PREFIX + SERVICE_MAPPING, mapString) System.setProperty(PREFIX + SERVICE_MAPPING, mapString)
System.setProperty(PREFIX + SPAN_TAGS, mapString) System.setProperty(PREFIX + SPAN_TAGS, mapString)
System.setProperty(PREFIX + HEADER_TAGS, mapString)
when: when:
def tracer = new DDTracer() def tracer = new DDTracer()
ServiceNameDecorator decorator = tracer.spanContextDecorators.values().flatten().find { ServiceNameDecorator decorator = tracer.spanContextDecorators.values().flatten().find {
it instanceof ServiceNameDecorator it instanceof ServiceNameDecorator
} }
def taggedHeaders = tracer.registry.codecs.values().first().taggedHeaders
then: then:
tracer.spanTags == map tracer.spanTags == map
decorator.mappings == map decorator.mappings == map
taggedHeaders == map
where: where:
mapString | map mapString | map