Add config allowing classes to be excluded from transformation
Add the following system property or corresponding environment variable:
```
dd.trace.classes.exclude=some.packagename.*,some.classname.MyClass$NestedClass
```
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java
@@ -6,10 +6,13 @@ import static net.bytebuddy.matcher.ElementMatchers.nameContains;
import static net.bytebuddy.matcher.ElementMatchers.nameMatches;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.none;
import static net.bytebuddy.matcher.ElementMatchers.not;
+import datadog.trace.api.Config;
import datadog.trace.bootstrap.WeakMap;
import java.lang.instrument.Instrumentation;
+import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;
@@ -18,6 +21,7 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
+import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import net.bytebuddy.utility.JavaModule;
@@ -112,7 +116,9 @@ public class AgentInstaller {
.or(nameStartsWith("com.newrelic."))
.or(nameContains("javassist"))
.or(nameContains(".asm."))
- .or(nameMatches("com\\.mchange\\.v2\\.c3p0\\..*Proxy"));
+ .or(nameMatches("com\\.mchange\\.v2\\.c3p0\\..*Proxy"))
+ .or(matchesConfiguredExcludes());
+
for (final AgentBuilder.Listener listener : listeners) {
agentBuilder = agentBuilder.with(listener);
}
@@ -127,6 +133,22 @@ public class AgentInstaller {
return agentBuilder.installOn(inst);
}
+ private static ElementMatcher.Junction<Object> matchesConfiguredExcludes() {
+ final List<String> excludedClasses = Config.get().getExcludedClasses();
+ ElementMatcher.Junction matcher = none();
+ for (String excludedClass : excludedClasses) {
+ excludedClass = excludedClass.trim();
+ if (excludedClass.endsWith("*")) {
+ // remove the trailing *
+ final String prefix = excludedClass.substring(0, excludedClass.length() - 1);
+ matcher = matcher.or(nameStartsWith(prefix));
+ } else {
+ matcher = matcher.or(named(excludedClass));
+ }
+ }
+ return matcher;
+ }
+
private static void registerWeakMapProvider() {
if (!WeakMap.Provider.isProviderRegistered()) {
WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.WeakConcurrent());

MODIFY
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy
@@ -62,6 +62,13 @@ class TraceUtils {
}
}
+ /**
+ * Calling will reset the runtimeId too, so it might cause problems around runtimeId verification.
+ */
+ static void resetConfig() {
+ setFinalStatic(Config.getDeclaredField("INSTANCE"), new Config())
+ }
+
private static void setFinalStatic(final Field field, final Object newValue) throws Exception {
setFinal(field, null, newValue)
}

MODIFY
dd-java-agent/testing/src/test/groovy/AgentTestRunnerTest.groovy
dd-java-agent/testing/src/test/groovy/AgentTestRunnerTest.groovy
@@ -10,6 +10,10 @@ import spock.lang.Shared
import java.lang.reflect.Field
+import static datadog.trace.agent.test.utils.TraceUtils.resetConfig
+import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace
+import static datadog.trace.api.Config.TRACE_CLASSES_EXCLUDE
+
class AgentTestRunnerTest extends AgentTestRunner {
private static final ClassLoader BOOTSTRAP_CLASSLOADER = null
private static final ClassLoader OT_LOADER
@@ -19,6 +23,9 @@ class AgentTestRunnerTest extends AgentTestRunner {
private Class sharedSpanClass
static {
+ System.setProperty("dd." + TRACE_CLASSES_EXCLUDE, "config.exclude.packagename.*, config.exclude.SomeClass,config.exclude.SomeClass\$NestedClass")
+ resetConfig()
+
// when test class initializes, opentracing should be set up, but not the agent.
OT_LOADER = io.opentracing.Tracer.getClassLoader()
AGENT_INSTALLED_IN_CLINIT = getAgentTransformer() != null
@@ -66,6 +73,30 @@ class AgentTestRunnerTest extends AgentTestRunner {
noExceptionThrown()
}
+ def "excluded classes are not instrumented"() {
+ when:
+ runUnderTrace("parent") {
+ subject.run()
+ }
+
+ then:
+ !TRANSFORMED_CLASSES.contains(subject.class.name)
+ assertTraces(1) {
+ trace(0, 1) {
+ span(0) {
+ operationName "parent"
+ }
+ }
+ }
+
+ where:
+ subject | _
+ new config.exclude.SomeClass() | _
+ new config.exclude.SomeClass.NestedClass() | _
+ new config.exclude.packagename.SomeClass() | _
+ new config.exclude.packagename.SomeClass.NestedClass() | _
+ }
+
private static getAgentTransformer() {
Field f
try {

ADD
dd-java-agent/testing/src/test/java/config/exclude/SomeClass.java
+package config.exclude;
+
+import datadog.trace.api.Trace;
+
+public class SomeClass implements Runnable {
+
+ @Trace
+ @Override
+ public void run() {}
+
+ public static class NestedClass implements Runnable {
+
+ @Trace
+ @Override
+ public void run() {}
+ }
+}

ADD
dd-java-agent/testing/src/test/java/config/exclude/packagename/SomeClass.java
+package config.exclude.packagename;
+
+import datadog.trace.api.Trace;
+
+public class SomeClass implements Runnable {
+
+ @Trace
+ @Override
+ public void run() {}
+
+ public static class NestedClass implements Runnable {
+
+ @Trace
+ @Override
+ public void run() {}
+ }
+}

MODIFY
dd-java-agent/testing/testing.gradle
dd-java-agent/testing/testing.gradle
@@ -32,6 +32,7 @@ dependencies {
compile deps.groovy
testCompile project(':utils:gc-utils')
+ testCompile project(':dd-java-agent:instrumentation:trace-annotation')
testCompile group: 'cglib', name: 'cglib', version: '3.2.5'
// test instrumenting java 1.1 bytecode

MODIFY
dd-trace-api/src/main/java/datadog/trace/api/Config.java
dd-trace-api/src/main/java/datadog/trace/api/Config.java
@@ -49,6 +49,7 @@ public class Config {
public static final String TRACE_ANALYTICS_ENABLED = "trace.analytics.enabled";
public static final String TRACE_ANNOTATIONS = "trace.annotations";
public static final String TRACE_METHODS = "trace.methods";
+ public static final String TRACE_CLASSES_EXCLUDE = "trace.classes.exclude";
public static final String HEADER_TAGS = "trace.header.tags";
public static final String HTTP_SERVER_ERROR_STATUSES = "http.server.error.statuses";
public static final String HTTP_CLIENT_ERROR_STATUSES = "http.client.error.statuses";
@@ -124,6 +125,7 @@ public class Config {
private final Map<String, String> globalTags;
private final Map<String, String> spanTags;
private final Map<String, String> jmxTags;
+ @Getter private final List<String> excludedClasses;
@Getter private final Map<String, String> headerTags;
@Getter private final Set<Integer> httpServerErrorStatuses;
@Getter private final Set<Integer> httpClientErrorStatuses;
@@ -164,6 +166,7 @@ public class Config {
globalTags = getMapSettingFromEnvironment(GLOBAL_TAGS, null);
spanTags = getMapSettingFromEnvironment(SPAN_TAGS, null);
jmxTags = getMapSettingFromEnvironment(JMX_TAGS, null);
+ excludedClasses = getListSettingFromEnvironment(TRACE_CLASSES_EXCLUDE, null);
headerTags = getMapSettingFromEnvironment(HEADER_TAGS, null);
httpServerErrorStatuses =
@@ -213,6 +216,8 @@ public class Config {
appCustomLogManager =
getBooleanSettingFromEnvironment(APP_CUSTOM_LOG_MANAGER, DEFAULT_APP_CUSTOM_LOG_MANAGER);
+
+ log.debug("New instance: {}", this);
}
// Read order: Properties -> Parent
@@ -238,6 +243,8 @@ public class Config {
globalTags = getPropertyMapValue(properties, GLOBAL_TAGS, parent.globalTags);
spanTags = getPropertyMapValue(properties, SPAN_TAGS, parent.spanTags);
jmxTags = getPropertyMapValue(properties, JMX_TAGS, parent.jmxTags);
+ excludedClasses =
+ getPropertyListValue(properties, TRACE_CLASSES_EXCLUDE, parent.excludedClasses);
headerTags = getPropertyMapValue(properties, HEADER_TAGS, parent.headerTags);
httpServerErrorStatuses =
@@ -290,6 +297,8 @@ public class Config {
appCustomLogManager =
getBooleanSettingFromEnvironment(APP_CUSTOM_LOG_MANAGER, DEFAULT_APP_CUSTOM_LOG_MANAGER);
+
+ log.debug("New instance: {}", this);
}
public Map<String, String> getMergedSpanTags() {

MODIFY
dd-trace-ot/src/main/java/datadog/opentracing/DDTracer.java
dd-trace-ot/src/main/java/datadog/opentracing/DDTracer.java
@@ -243,19 +243,18 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
}
}
- registerClassLoader(ClassLoader.getSystemClassLoader());
+ log.info("New instance: {}", this);
final List<AbstractDecorator> decorators = DDDecoratorsFactory.createBuiltinDecorators();
for (final AbstractDecorator decorator : decorators) {
- log.debug("Loading decorator: {}", decorator.getClass().getSimpleName());
addDecorator(decorator);
}
+ registerClassLoader(ClassLoader.getSystemClassLoader());
+
// 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();
-
- log.info("New instance: {}", this);
}
@Override
@@ -291,6 +290,8 @@ public class DDTracer implements io.opentracing.Tracer, Closeable, datadog.trace
list.add(decorator);
spanContextDecorators.put(decorator.getMatchingTag(), list);
+ log.debug(
+ "Decorator added: '{}' -> {}", decorator.getMatchingTag(), decorator.getClass().getName());
}
@Deprecated

Add config allowing classes to be excluded from transformation

Add the following system property or corresponding environment variable:
```
dd.trace.classes.exclude=some.packagename.*,some.classname.MyClass$NestedClass
```