Define the concept of applicationRootSpanTags and apply only to root
tags
This commit is contained in:
parent
0e9f9d9d02
commit
10069847a3
|
@ -1,5 +1,7 @@
|
|||
package datadog.trace.api;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -113,6 +115,9 @@ public class Config {
|
|||
B3
|
||||
}
|
||||
|
||||
/** A tag intended for internal use only, hence not added to the public api DDTags class. */
|
||||
private static final String INTERNAL_HOST_NAME = "_dd.hostname";
|
||||
|
||||
/**
|
||||
* this is a random UUID that gets generated on JVM start up and is attached to every root span
|
||||
* and every JMX metric that is sent out.
|
||||
|
@ -150,10 +155,7 @@ public class Config {
|
|||
|
||||
@Getter private final boolean logsInjectionEnabled;
|
||||
|
||||
// If `true` the hostname will be detected and added to the root span's metadata
|
||||
// Note: this temporarily non final as a conversation is in place related to how to improve
|
||||
// testability under different configuration scenarios.
|
||||
@Getter private boolean reportHostName;
|
||||
@Getter private final boolean reportHostName;
|
||||
|
||||
// Read order: System Properties -> Env Variables, [-> default value]
|
||||
// Visible for testing
|
||||
|
@ -318,6 +320,15 @@ public class Config {
|
|||
log.debug("New instance: {}", this);
|
||||
}
|
||||
|
||||
/** @return A map of tags to be applied only to the currently tracing application root span. */
|
||||
public Map<String, String> getApplicationRootSpanTags() {
|
||||
final Map<String, String> result = newHashMap(reportHostName ? 1 : 0);
|
||||
if (reportHostName) {
|
||||
result.put(INTERNAL_HOST_NAME, getHostname());
|
||||
}
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
|
||||
public Map<String, String> getMergedSpanTags() {
|
||||
// DO not include runtimeId into span tags: we only want that added to the root span
|
||||
final Map<String, String> result = newHashMap(globalTags.size() + spanTags.size());
|
||||
|
@ -646,6 +657,28 @@ public class Config {
|
|||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
// Fields used to cache detected hostName which is a time consuming operation.
|
||||
private String hostName = null;
|
||||
private boolean hostNameDetected = false;
|
||||
|
||||
/**
|
||||
* Returns the detected hostname. This operation is time consuming and the first time this method
|
||||
* is called will take some time. Hostname is cached for subsequent calls.
|
||||
*/
|
||||
public String getHostname() {
|
||||
if (!this.hostNameDetected) {
|
||||
try {
|
||||
this.hostName = InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
// If we are not able to detect the hostname we do not throw an exception.
|
||||
} finally {
|
||||
this.hostNameDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.hostName;
|
||||
}
|
||||
|
||||
// This has to be placed after all other static fields to give them a chance to initialize
|
||||
private static final Config INSTANCE = new Config();
|
||||
|
||||
|
@ -660,13 +693,4 @@ public class Config {
|
|||
return new Config(properties, INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this is a workaround as a conversation related to improve testability of services under
|
||||
* different configuration scenarios is in progress.
|
||||
*/
|
||||
public void refreshDetectHostnameProperty() {
|
||||
this.reportHostName =
|
||||
getBooleanSettingFromEnvironment(TRACE_REPORT_HOSTNAME, DEFAULT_TRACE_REPORT_HOSTNAME);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -658,4 +658,27 @@ class ConfigTest extends Specification {
|
|||
listString | list
|
||||
"" | []
|
||||
}
|
||||
|
||||
def "verify hostname not added to root span tags by default"() {
|
||||
setup:
|
||||
Properties properties = new Properties()
|
||||
|
||||
when:
|
||||
def config = Config.get(properties)
|
||||
|
||||
then:
|
||||
!config.applicationRootSpanTags.containsKey('_dd.hostname')
|
||||
}
|
||||
|
||||
def "verify configuration to add hostname to root span tags"() {
|
||||
setup:
|
||||
Properties properties = new Properties()
|
||||
properties.setProperty(TRACE_REPORT_HOSTNAME, 'true')
|
||||
|
||||
when:
|
||||
def config = Config.get(properties)
|
||||
|
||||
then:
|
||||
config.applicationRootSpanTags.get('_dd.hostname') == InetAddress.localHost.hostName
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,6 @@ import io.opentracing.propagation.Format;
|
|||
import io.opentracing.propagation.TextMap;
|
||||
import java.io.Closeable;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
|
@ -50,6 +48,8 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
|
||||
/** Tags required to link apm traces to runtime metrics */
|
||||
final Map<String, String> runtimeTags;
|
||||
/** A set of tags that are added only to the application's root span */
|
||||
private final Map<String, String> applicationRootSpanTags;
|
||||
/** A set of tags that are added to every span */
|
||||
private final Map<String, String> defaultSpanTags;
|
||||
/** A configured mapping of service names to update with new values */
|
||||
|
@ -80,15 +80,6 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
private final HttpCodec.Injector injector;
|
||||
private final HttpCodec.Extractor extractor;
|
||||
|
||||
/** A tag intended for internal use only, hence not added to the public api DDTags class. */
|
||||
private static final String INTERNAL_HOST_NAME = "_dd.hostname";
|
||||
|
||||
/**
|
||||
* hostname is expensive to calculate so we detect it only when creating the tracer and we cache
|
||||
* it for future reuse.
|
||||
*/
|
||||
private String hostname;
|
||||
|
||||
/** By default, report to local agent and collect all traces. */
|
||||
public DDTracer() {
|
||||
this(Config.get());
|
||||
|
@ -117,6 +108,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
Writer.Builder.forConfig(config),
|
||||
Sampler.Builder.forConfig(config),
|
||||
config.getRuntimeTags(),
|
||||
config.getApplicationRootSpanTags(),
|
||||
config.getMergedSpanTags(),
|
||||
config.getServiceMapping(),
|
||||
config.getHeaderTags(),
|
||||
|
@ -138,6 +130,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
Collections.<String, String>emptyMap(),
|
||||
Collections.<String, String>emptyMap(),
|
||||
Collections.<String, String>emptyMap(),
|
||||
Collections.<String, String>emptyMap(),
|
||||
0);
|
||||
}
|
||||
|
||||
|
@ -151,6 +144,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
writer,
|
||||
Sampler.Builder.forConfig(config),
|
||||
config.getRuntimeTags(),
|
||||
config.getApplicationRootSpanTags(),
|
||||
config.getMergedSpanTags(),
|
||||
config.getServiceMapping(),
|
||||
config.getHeaderTags(),
|
||||
|
@ -166,6 +160,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
final Writer writer,
|
||||
final Sampler sampler,
|
||||
final String runtimeId,
|
||||
final Map<String, String> applicationRootSpanTags,
|
||||
final Map<String, String> defaultSpanTags,
|
||||
final Map<String, String> serviceNameMappings,
|
||||
final Map<String, String> taggedHeaders) {
|
||||
|
@ -174,6 +169,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
writer,
|
||||
sampler,
|
||||
customRuntimeTags(runtimeId),
|
||||
applicationRootSpanTags,
|
||||
defaultSpanTags,
|
||||
serviceNameMappings,
|
||||
taggedHeaders,
|
||||
|
@ -189,6 +185,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
final Writer writer,
|
||||
final Sampler sampler,
|
||||
final Map<String, String> runtimeTags,
|
||||
final Map<String, String> applicationRootSpanTags,
|
||||
final Map<String, String> defaultSpanTags,
|
||||
final Map<String, String> serviceNameMappings,
|
||||
final Map<String, String> taggedHeaders) {
|
||||
|
@ -197,6 +194,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
writer,
|
||||
sampler,
|
||||
runtimeTags,
|
||||
applicationRootSpanTags,
|
||||
defaultSpanTags,
|
||||
serviceNameMappings,
|
||||
taggedHeaders,
|
||||
|
@ -208,6 +206,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
final Writer writer,
|
||||
final Sampler sampler,
|
||||
final Map<String, String> runtimeTags,
|
||||
final Map<String, String> applicationRootSpanTags,
|
||||
final Map<String, String> defaultSpanTags,
|
||||
final Map<String, String> serviceNameMappings,
|
||||
final Map<String, String> taggedHeaders,
|
||||
|
@ -221,6 +220,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
this.writer = writer;
|
||||
this.writer.start();
|
||||
this.sampler = sampler;
|
||||
this.applicationRootSpanTags = applicationRootSpanTags;
|
||||
this.defaultSpanTags = defaultSpanTags;
|
||||
this.runtimeTags = runtimeTags;
|
||||
this.serviceNameMappings = serviceNameMappings;
|
||||
|
@ -256,8 +256,6 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
// Ensure that PendingTrace.SPAN_CLEANER is initialized in this thread:
|
||||
// FIXME: add test to verify the span cleaner thread is started with this call.
|
||||
PendingTrace.initialize();
|
||||
|
||||
cacheHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -383,7 +381,6 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
// TODO: current trace implementation doesn't guarantee that first span is the root span
|
||||
// We may want to reconsider way this check is done.
|
||||
if (!writtenTrace.isEmpty() && sampler.sample(writtenTrace.get(0))) {
|
||||
applyHostNameDetection(writtenTrace);
|
||||
writer.write(writtenTrace);
|
||||
}
|
||||
}
|
||||
|
@ -616,6 +613,7 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
final PendingTrace parentTrace;
|
||||
final int samplingPriority;
|
||||
final String origin;
|
||||
final boolean isApplicationRootSpan = parent == null || !(parent instanceof DDSpanContext);
|
||||
|
||||
final DDSpanContext context;
|
||||
SpanContext parentContext = parent;
|
||||
|
@ -678,6 +676,10 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
|
||||
final String operationName = this.operationName != null ? this.operationName : resourceName;
|
||||
|
||||
if (isApplicationRootSpan) {
|
||||
applyTagsIfNotDefined(tags, applicationRootSpanTags);
|
||||
}
|
||||
|
||||
// some attributes are inherited from the parent
|
||||
context =
|
||||
new DDSpanContext(
|
||||
|
@ -727,6 +729,28 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge a map of tag 'candidates' into a 'destination' map of tags never overwriting original
|
||||
* values.
|
||||
*
|
||||
* @param destination The map of tags where candidate tags will be merged in
|
||||
* @param candidates The potential tags that will be added if not already defined
|
||||
*/
|
||||
private void applyTagsIfNotDefined(
|
||||
Map<String, Object> destination, Map<String, String> candidates) {
|
||||
if (null == destination || null == candidates) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<String, String> entry : candidates.entrySet()) {
|
||||
if (destination.containsKey(entry.getKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
destination.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ShutdownHook extends Thread {
|
||||
|
@ -744,57 +768,4 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hostname is expensive to retrieve so we provide a way to calculate it and cache it in the
|
||||
* tracer itself for further reuse.
|
||||
*/
|
||||
private void cacheHostName() {
|
||||
|
||||
// Host name detection can be disabled via configuration
|
||||
if (!Config.get().isReportHostName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.hostname = InetAddress.getLocalHost().getHostName();
|
||||
} catch (UnknownHostException e) {
|
||||
// If we are not able to detect the hostname we do not throw an exception.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the internal hostname tag on a root span if appropriate. The following acceptance criteria
|
||||
* apply: 1) Users should not be able to overwrite this value, 2) It has to be done only on the
|
||||
* root span, 3) It is not guaranteed that the first span in the list is the root span.
|
||||
*
|
||||
* @param spans
|
||||
*/
|
||||
private void applyHostNameDetection(List<DDSpan> spans) {
|
||||
|
||||
// Host name detection can be disabled via configuration
|
||||
if (!Config.get().isReportHostName()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Every time we set a tag all the decorators have to be executed so if we already set the
|
||||
// hostname on the root of a specific span, using this registry we can avoid setting the tag
|
||||
// again, hence executing all decorators again to obtain the same result.
|
||||
Set<MutableSpan> alreadyTrackedRootSpans = new HashSet<>();
|
||||
for (DDSpan span : spans) {
|
||||
MutableSpan rootSpan = span.getRootSpan();
|
||||
|
||||
// Only root spans get the hostname applied
|
||||
if (null == rootSpan) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (alreadyTrackedRootSpans.contains(rootSpan)) {
|
||||
continue;
|
||||
}
|
||||
alreadyTrackedRootSpans.add(rootSpan);
|
||||
|
||||
rootSpan.setTag(INTERNAL_HOST_NAME, hostname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,35 +149,18 @@ class DDTracerTest extends Specification {
|
|||
PRIORITY_SAMPLING | "false"
|
||||
}
|
||||
|
||||
def "tracer does not set the host name by default"() {
|
||||
def "root tags are applied only to root spans"() {
|
||||
setup:
|
||||
def tracer = new DDTracer('my_service', new ListWriter(), new AllSampler())
|
||||
def tracer = new DDTracer('my_service', new ListWriter(), new AllSampler(), '', ['only_root': 'value'], [:], [:], [:])
|
||||
def root = tracer.buildSpan('my_root').start()
|
||||
def child = tracer.buildSpan('my_child').asChildOf(root).start()
|
||||
|
||||
when:
|
||||
DDSpan root = tracer.buildSpan('my_root').start()
|
||||
DDSpan child = tracer.buildSpan('my_child').asChildOf(root).start()
|
||||
expect:
|
||||
root.context().tags.containsKey('only_root')
|
||||
!child.context().tags.containsKey('only_root')
|
||||
|
||||
cleanup:
|
||||
child.finish()
|
||||
root.finish()
|
||||
|
||||
then:
|
||||
!root.context().tags.containsKey(INTERNAL_HOST_NAME)
|
||||
!child.context().tags.containsKey(INTERNAL_HOST_NAME)
|
||||
}
|
||||
|
||||
def "tracer sets the host name if activated only on root span"() {
|
||||
setup:
|
||||
System.setProperty('dd.trace.report-hostname', 'true')
|
||||
Config.get().refreshDetectHostnameProperty()
|
||||
def tracer = new DDTracer('my_service', new ListWriter(), new AllSampler())
|
||||
|
||||
when:
|
||||
DDSpan root = tracer.buildSpan('my_root').start()
|
||||
DDSpan child = tracer.buildSpan('my_child').asChildOf(root).start()
|
||||
child.finish()
|
||||
root.finish()
|
||||
|
||||
then:
|
||||
root.context().tags.get(INTERNAL_HOST_NAME) == InetAddress.localHost.hostName
|
||||
!child.context().tags.containsKey(INTERNAL_HOST_NAME)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue