Allow instrumentation to be disabled

By default, instrumentation is enabled, and can be disabled by default by overriding the `defaultEnabled` method. Instrumentation can also be disabled individually or enabled when default is disabled.
This commit is contained in:
Tyler Benson 2018-02-06 10:46:03 +10:00
parent 3eeb27b900
commit 7d28a32fba
39 changed files with 452 additions and 124 deletions

View File

@ -35,10 +35,8 @@ dependencies {
Project java_agent_project = project
subprojects { subProj ->
if (subProj.getPath().startsWith(java_agent_project.getPath() + ':instrumentation:')) {
if (!subProj.getPath().startsWith(java_agent_project.getPath() + ':instrumentation:kafka')) {
java_agent_project.dependencies {
compile(project(subProj.getPath()))
}
java_agent_project.dependencies {
compile(project(subProj.getPath()))
}
}
}

View File

@ -15,10 +15,14 @@ import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.execchain.ClientExecChain;
@AutoService(Instrumenter.class)
public class ApacheHttpClientInstrumentation implements Instrumenter {
public class ApacheHttpClientInstrumentation extends Instrumenter.Configurable {
public ApacheHttpClientInstrumentation() {
super("httpclient");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("org.apache.http.impl.client.HttpClientBuilder"),

View File

@ -23,10 +23,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class AWSClientInstrumentation implements Instrumenter {
public final class AWSClientInstrumentation extends Instrumenter.Configurable {
public AWSClientInstrumentation() {
super("aws-sdk");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
hasSuperType(named("com.amazonaws.client.builder.AwsClientBuilder")),

View File

@ -18,9 +18,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public class CassandraClientInstrumentation implements Instrumenter {
public class CassandraClientInstrumentation extends Instrumenter.Configurable {
public CassandraClientInstrumentation() {
super("cassandra");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("com.datastax.driver.core.Cluster$Manager"),

View File

@ -22,14 +22,18 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class ConnectionInstrumentation implements Instrumenter {
public final class ConnectionInstrumentation extends Instrumenter.Configurable {
public static final Map<Connection, DBInfo> connectionInfo =
Collections.synchronizedMap(new WeakHashMap<Connection, DBInfo>());
public static final Map<PreparedStatement, String> preparedStatements =
Collections.synchronizedMap(new WeakHashMap<PreparedStatement, String>());
public ConnectionInstrumentation() {
super("jdbc");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(hasSuperType(named(Connection.class.getName()))))
.transform(

View File

@ -24,11 +24,15 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class PreparedStatementInstrumentation implements Instrumenter {
public final class PreparedStatementInstrumentation extends Instrumenter.Configurable {
private static final String UNKNOWN_QUERY = "Unknown Query";
public PreparedStatementInstrumentation() {
super("jdbc");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(hasSuperType(named(PreparedStatement.class.getName()))))
.transform(

View File

@ -24,10 +24,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class StatementInstrumentation implements Instrumenter {
public final class StatementInstrumentation extends Instrumenter.Configurable {
public StatementInstrumentation() {
super("jdbc");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(not(isInterface()).and(hasSuperType(named(Statement.class.getName()))))
.transform(

View File

@ -30,14 +30,18 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class JMS1MessageConsumerInstrumentation implements Instrumenter {
public final class JMS1MessageConsumerInstrumentation extends Instrumenter.Configurable {
public static final HelperInjector JMS1_HELPER_INJECTOR =
new HelperInjector(
"datadog.trace.instrumentation.jms.util.JmsUtil",
"datadog.trace.instrumentation.jms.util.MessagePropertyTextMap");
public JMS1MessageConsumerInstrumentation() {
super("jms", "jms-1");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageConsumer"))),

View File

@ -28,10 +28,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class JMS1MessageListenerInstrumentation implements Instrumenter {
public final class JMS1MessageListenerInstrumentation extends Instrumenter.Configurable {
public JMS1MessageListenerInstrumentation() {
super("jms", "jms-1");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageListener"))),

View File

@ -29,10 +29,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class JMS1MessageProducerInstrumentation implements Instrumenter {
public final class JMS1MessageProducerInstrumentation extends Instrumenter.Configurable {
public JMS1MessageProducerInstrumentation() {
super("jms", "jms-1");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageProducer"))),

View File

@ -30,14 +30,18 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class JMS2MessageConsumerInstrumentation implements Instrumenter {
public final class JMS2MessageConsumerInstrumentation extends Instrumenter.Configurable {
public static final HelperInjector JMS2_HELPER_INJECTOR =
new HelperInjector(
"datadog.trace.instrumentation.jms.util.JmsUtil",
"datadog.trace.instrumentation.jms.util.MessagePropertyTextMap");
public JMS2MessageConsumerInstrumentation() {
super("jms", "jms-2");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageConsumer"))),

View File

@ -28,10 +28,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class JMS2MessageListenerInstrumentation implements Instrumenter {
public final class JMS2MessageListenerInstrumentation extends Instrumenter.Configurable {
public JMS2MessageListenerInstrumentation() {
super("jms", "jms-2");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageListener"))),

View File

@ -29,10 +29,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class JMS2MessageProducerInstrumentation implements Instrumenter {
public final class JMS2MessageProducerInstrumentation extends Instrumenter.Configurable {
public JMS2MessageProducerInstrumentation() {
super("jms", "jms-2");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.jms.MessageProducer"))),

View File

@ -25,7 +25,7 @@ import net.bytebuddy.asm.Advice;
import org.apache.kafka.clients.consumer.ConsumerRecord;
@AutoService(Instrumenter.class)
public final class KafkaConsumerInstrumentation implements Instrumenter {
public final class KafkaConsumerInstrumentation extends Instrumenter.Configurable {
public static final HelperInjector HELPER_INJECTOR =
new HelperInjector(
"datadog.trace.instrumentation.kafka_clients.TextMapExtractAdapter",
@ -38,8 +38,17 @@ public final class KafkaConsumerInstrumentation implements Instrumenter {
private static final String OPERATION = "kafka.consume";
private static final String COMPONENT_NAME = "java-kafka";
public KafkaConsumerInstrumentation() {
super("kafka");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("org.apache.kafka.clients.consumer.ConsumerRecords"),

View File

@ -25,7 +25,7 @@ import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
@AutoService(Instrumenter.class)
public final class KafkaProducerInstrumentation implements Instrumenter {
public final class KafkaProducerInstrumentation extends Instrumenter.Configurable {
public static final HelperInjector HELPER_INJECTOR =
new HelperInjector(
"datadog.trace.instrumentation.kafka_clients.TextMapInjectAdapter",
@ -34,8 +34,17 @@ public final class KafkaProducerInstrumentation implements Instrumenter {
private static final String OPERATION = "kafka.produce";
private static final String COMPONENT_NAME = "java-kafka";
public KafkaProducerInstrumentation() {
super("kafka");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("org.apache.kafka.clients.producer.KafkaProducer"),

View File

@ -18,12 +18,13 @@ import spock.lang.Shared
import java.util.concurrent.LinkedBlockingQueue
import java.util.concurrent.TimeUnit
class KafkaTest extends AgentTestRunner {
class KafkaClientTest extends AgentTestRunner {
static final SHARED_TOPIC = "shared.topic"
static {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(Level.WARN)
((Logger) LoggerFactory.getLogger("datadog")).setLevel(Level.DEBUG)
System.setProperty("dd.integration.kafka.enabled", "true")
}
@Shared

View File

@ -26,7 +26,7 @@ import net.bytebuddy.asm.Advice;
import org.apache.kafka.streams.processor.internals.StampedRecord;
public class KafkaStreamsProcessorInstrumentation {
// These two instrumentations work together to instrument StreamTask.process.
// These two instrumentations work together to apply StreamTask.process.
// The combination of these are needed because there's not a good instrumentation point.
public static final HelperInjector HELPER_INJECTOR =
@ -36,10 +36,19 @@ public class KafkaStreamsProcessorInstrumentation {
private static final String COMPONENT_NAME = "java-kafka";
@AutoService(Instrumenter.class)
public static class StartInstrumentation implements Instrumenter {
public static class StartInstrumentation extends Instrumenter.Configurable {
public StartInstrumentation() {
super("kafka", "kafka-streams");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("org.apache.kafka.streams.processor.internals.PartitionGroup"),
@ -88,10 +97,19 @@ public class KafkaStreamsProcessorInstrumentation {
}
@AutoService(Instrumenter.class)
public static class StopInstrumentation implements Instrumenter {
public static class StopInstrumentation extends Instrumenter.Configurable {
public StopInstrumentation() {
super("kafka", "kafka-streams");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("org.apache.kafka.streams.processor.internals.StreamTask"),

View File

@ -19,10 +19,19 @@ import org.apache.kafka.common.record.TimestampType;
public class KafkaStreamsSourceNodeRecordDeserializerInstrumentation {
@AutoService(Instrumenter.class)
public static class StartInstrumentation implements Instrumenter {
public static class StartInstrumentation extends Instrumenter.Configurable {
public StartInstrumentation() {
super("kafka", "kafka-streams");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
protected boolean defaultEnabled() {
return false;
}
@Override
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("org.apache.kafka.streams.processor.internals.SourceNodeRecordDeserializer"),

View File

@ -32,6 +32,7 @@ class KafkaStreamsTest extends AgentTestRunner {
static {
((Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(Level.WARN)
((Logger) LoggerFactory.getLogger("datadog")).setLevel(Level.DEBUG)
System.setProperty("dd.integration.kafka.enabled", "true")
}
@Shared

View File

@ -19,12 +19,16 @@ import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@AutoService(Instrumenter.class)
public final class MongoClientInstrumentation implements Instrumenter {
public final class MongoClientInstrumentation extends Instrumenter.Configurable {
public static final HelperInjector MONGO_HELPER_INJECTOR =
new HelperInjector("datadog.trace.instrumentation.mongo.DDTracingCommandListener");
public MongoClientInstrumentation() {
super("mongo");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("com.mongodb.MongoClientOptions$Builder")

View File

@ -18,10 +18,14 @@ import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
@AutoService(Instrumenter.class)
public final class MongoAsyncClientInstrumentation implements Instrumenter {
public final class MongoAsyncClientInstrumentation extends Instrumenter.Configurable {
public MongoAsyncClientInstrumentation() {
super("mongo");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("com.mongodb.async.client.MongoClientSettings$Builder")

View File

@ -19,10 +19,14 @@ import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
@AutoService(Instrumenter.class)
public class OkHttp3Instrumentation implements Instrumenter {
public class OkHttp3Instrumentation extends Instrumenter.Configurable {
public OkHttp3Instrumentation() {
super("okhttp", "okhttp-3");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
named("okhttp3.OkHttpClient"),

View File

@ -31,11 +31,15 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class FilterChain2Instrumentation implements Instrumenter {
public final class FilterChain2Instrumentation extends Instrumenter.Configurable {
public static final String FILTER_CHAIN_OPERATION_NAME = "servlet.request";
public FilterChain2Instrumentation() {
super("servlet", "servlet-2");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.servlet.FilterChain"))),

View File

@ -31,11 +31,15 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class HttpServlet2Instrumentation implements Instrumenter {
public final class HttpServlet2Instrumentation extends Instrumenter.Configurable {
public static final String SERVLET_OPERATION_NAME = "servlet.request";
public HttpServlet2Instrumentation() {
super("servlet", "servlet-2");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.servlet.http.HttpServlet"))),

View File

@ -35,11 +35,15 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class FilterChain3Instrumentation implements Instrumenter {
public final class FilterChain3Instrumentation extends Instrumenter.Configurable {
public static final String SERVLET_OPERATION_NAME = "servlet.request";
public FilterChain3Instrumentation() {
super("servlet", "servlet-3");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.servlet.FilterChain"))),

View File

@ -33,11 +33,15 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class HttpServlet3Instrumentation implements Instrumenter {
public final class HttpServlet3Instrumentation extends Instrumenter.Configurable {
public static final String SERVLET_OPERATION_NAME = "servlet.request";
public HttpServlet3Instrumentation() {
super("servlet", "servlet-3");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface()).and(hasSuperType(named("javax.servlet.http.HttpServlet"))),

View File

@ -23,10 +23,14 @@ import net.bytebuddy.asm.Advice;
import org.springframework.web.servlet.HandlerMapping;
@AutoService(Instrumenter.class)
public final class SpringWebInstrumentation implements Instrumenter {
public final class SpringWebInstrumentation extends Instrumenter.Configurable {
public SpringWebInstrumentation() {
super("spring-web");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(
not(isInterface())

View File

@ -18,10 +18,14 @@ import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
@AutoService(Instrumenter.class)
public final class TraceAnnotationInstrumentation implements Instrumenter {
public final class TraceAnnotationInstrumentation extends Instrumenter.Configurable {
public TraceAnnotationInstrumentation() {
super("trace", "trace-annotation");
}
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
public AgentBuilder apply(final AgentBuilder agentBuilder) {
return agentBuilder
.type(hasSuperType(declaresMethod(isAnnotatedWith(Trace.class))))
.transform(

View File

@ -55,8 +55,8 @@ public class AgentInstaller {
"org.codehaus.groovy.runtime.callsite.CallSiteClassLoader")));
int numInstrumenters = 0;
for (final Instrumenter instrumenter : ServiceLoader.load(Instrumenter.class)) {
agentBuilder = instrumenter.instrument(agentBuilder);
log.debug("Loading instrumentation {}", instrumenter);
agentBuilder = instrumenter.instrument(agentBuilder);
numInstrumenters++;
}
log.debug("Installed {} instrumenter(s)", numInstrumenters);

View File

@ -1,7 +1,56 @@
package datadog.trace.agent.tooling;
import static datadog.trace.agent.tooling.Utils.getConfigEnabled;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.agent.builder.AgentBuilder;
public interface Instrumenter {
AgentBuilder instrument(AgentBuilder agentBuilder);
@Slf4j
abstract class Configurable implements Instrumenter {
private final Set<String> instrumentationNames;
protected final boolean enabled;
public Configurable(final String instrumentationName, final String... additionalNames) {
this.instrumentationNames = new HashSet(Arrays.asList(additionalNames));
instrumentationNames.add(instrumentationName);
// If default is enabled, we want to enable individually,
// if default is disabled, we want to disable individually.
final boolean defaultEnabled = defaultEnabled();
boolean anyEnabled = defaultEnabled;
for (final String name : instrumentationNames) {
final boolean configEnabled =
getConfigEnabled("dd.integration." + name + ".enabled", defaultEnabled);
if (defaultEnabled) {
anyEnabled &= configEnabled;
} else {
anyEnabled |= configEnabled;
}
}
enabled = anyEnabled;
}
protected boolean defaultEnabled() {
return getConfigEnabled("dd.integrations.enabled", true);
}
@Override
public final AgentBuilder instrument(final AgentBuilder agentBuilder) {
if (enabled) {
return apply(agentBuilder);
} else {
log.debug("Instrumentation {} is disabled", this);
return agentBuilder;
}
}
protected abstract AgentBuilder apply(AgentBuilder agentBuilder);
}
}

View File

@ -36,5 +36,12 @@ public class Utils {
}
}
static boolean getConfigEnabled(final String name, final boolean fallback) {
final String property =
System.getProperty(
name, System.getenv(name.toUpperCase().replaceAll("[^a-zA-Z0-9_]", "_")));
return property == null ? fallback : Boolean.parseBoolean(property);
}
private Utils() {}
}

View File

@ -1,4 +1,4 @@
package datadog.trace.agent.test;
package datadog.trace.agent.tooling;
import net.bytebuddy.asm.Advice;

View File

@ -0,0 +1,178 @@
package datadog.trace.agent.tooling
import net.bytebuddy.agent.builder.AgentBuilder
import org.junit.Rule
import org.junit.contrib.java.lang.system.EnvironmentVariables
import org.junit.contrib.java.lang.system.RestoreSystemProperties
import spock.lang.Specification
import spock.lang.Unroll
class ConfigurableInstrumenterTest extends Specification {
@Rule
public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties()
@Rule
public final EnvironmentVariables environmentVariables = new EnvironmentVariables()
def setup() {
assert System.getenv().findAll { it.key.startsWith("DD_") }.isEmpty()
assert System.getProperties().findAll { it.key.toString().startsWith("dd.") }.isEmpty()
}
def "default enabled"() {
setup:
def target = new TestConfigurableInstrumenter("test")
target.instrument(null)
expect:
target.enabled
target.applyCalled
}
def "default enabled override"() {
expect:
target.instrument(null) == null
target.enabled == enabled
target.applyCalled == enabled
where:
enabled | target
true | new TestConfigurableInstrumenter("test") {
@Override
protected boolean defaultEnabled() {
return true
}
}
false | new TestConfigurableInstrumenter("test") {
@Override
protected boolean defaultEnabled() {
return false
}
}
}
def "default disabled can override to enabled"() {
setup:
System.setProperty("dd.integration.test.enabled", "$enabled")
def target = new TestConfigurableInstrumenter("test") {
@Override
protected boolean defaultEnabled() {
return false
}
}
expect:
target.instrument(null) == null
target.enabled == enabled
target.applyCalled == enabled
where:
enabled << [true, false]
}
@Unroll
def "configure default sys prop as #value"() {
setup:
System.setProperty("dd.integrations.enabled", value)
def target = new TestConfigurableInstrumenter("test")
expect:
target.instrument(null) == null
target.enabled == enabled
target.applyCalled == enabled
where:
value | enabled
"true" | true
"false" | false
"asdf" | false
}
@Unroll
def "configure default env var as #value"() {
setup:
environmentVariables.set("DD_INTEGRATIONS_ENABLED", value)
def target = new TestConfigurableInstrumenter("test")
expect:
target.instrument(null) == null
target.enabled == enabled
target.applyCalled == enabled
where:
value | enabled
"true" | true
"false" | false
"asdf" | false
}
@Unroll
def "configure sys prop enabled for #value when default is disabled"() {
setup:
System.setProperty("dd.integrations.enabled", "false")
System.setProperty("dd.integration.${value}.enabled", "true")
def target = new TestConfigurableInstrumenter(name, altName)
expect:
target.instrument(null) == null
target.enabled == enabled
target.applyCalled == enabled
where:
value | enabled | name | altName
"test" | true | "test" | "asdf"
"duplicate" | true | "duplicate" | "duplicate"
"bad" | false | "not" | "valid"
"altTest" | true | "asdf" | "altTest"
"dash-test" | true | "dash-test" | "asdf"
"underscore_test" | true | "asdf" | "underscore_test"
"period.test" | true | "period.test" | "asdf"
}
@Unroll
def "configure env var enabled for #value when default is disabled"() {
setup:
environmentVariables.set("DD_INTEGRATIONS_ENABLED", "false")
environmentVariables.set("DD_INTEGRATION_${value}_ENABLED", "true")
def target = new TestConfigurableInstrumenter(name, altName)
expect:
System.getenv("DD_INTEGRATION_${value}_ENABLED") == "true"
target.instrument(null) == null
target.enabled == enabled
target.applyCalled == enabled
where:
value | enabled | name | altName
"TEST" | true | "test" | "asdf"
"DUPLICATE" | true | "duplicate" | "duplicate"
"BAD" | false | "not" | "valid"
"ALTTEST" | true | "asdf" | "altTest"
"DASH_TEST" | true | "dash-test" | "asdf"
"UNDERSCORE_TEST" | true | "asdf" | "underscore_test"
"PERIOD_TEST" | true | "period.test" | "asdf"
}
class TestConfigurableInstrumenter extends Instrumenter.Configurable {
boolean applyCalled = false
TestConfigurableInstrumenter(
String instrumentationName) {
super(instrumentationName)
}
TestConfigurableInstrumenter(
String instrumentationName, String additionalName) {
super(instrumentationName, [additionalName])
}
@Override
protected AgentBuilder apply(AgentBuilder agentBuilder) {
applyCalled = true
return null
}
def getEnabled() {
return super.enabled
}
}
}

View File

@ -1,21 +1,18 @@
package datadog.trace.agent.test
package datadog.trace.agent.tooling
import datadog.trace.agent.tooling.ExceptionHandlers
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
import net.bytebuddy.agent.ByteBuddyAgent
import net.bytebuddy.agent.builder.AgentBuilder
import net.bytebuddy.dynamic.ClassFileLocator
import org.slf4j.LoggerFactory
import spock.lang.Shared
import spock.lang.Specification
import static net.bytebuddy.matcher.ElementMatchers.isMethod
import static net.bytebuddy.matcher.ElementMatchers.named
import net.bytebuddy.agent.builder.AgentBuilder
import spock.lang.Specification
import spock.lang.Shared
import org.slf4j.LoggerFactory
import ch.qos.logback.classic.Logger
import ch.qos.logback.core.read.ListAppender
import ch.qos.logback.classic.Level
class ExceptionHandlerTest extends Specification {
@Shared
ListAppender testAppender = new ListAppender()
@ -24,7 +21,7 @@ class ExceptionHandlerTest extends Specification {
AgentBuilder builder = new AgentBuilder.Default()
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(named(getClass().getName()+'$SomeClass'))
.type(named(getClass().getName() + '$SomeClass'))
.transform(
new AgentBuilder.Transformer.ForAdvice()
.with(new AgentBuilder.LocationStrategy.Simple(ClassFileLocator.ForClassLoader.of(BadAdvice.getClassLoader())))
@ -60,11 +57,11 @@ class ExceptionHandlerTest extends Specification {
testAppender.list.get(testAppender.list.size() - 1).getMessage() == "exception in instrumentation"
}
def "exception on non-delegating classloader" () {
def "exception on non-delegating classloader"() {
setup:
int initLogEvents = testAppender.list.size()
URL[] classpath = [ SomeClass.getProtectionDomain().getCodeSource().getLocation(),
GroovyObject.getProtectionDomain().getCodeSource().getLocation() ]
URL[] classpath = [SomeClass.getProtectionDomain().getCodeSource().getLocation(),
GroovyObject.getProtectionDomain().getCodeSource().getLocation()]
URLClassLoader loader = new URLClassLoader(classpath, (ClassLoader) null)
when:
loader.loadClass(LoggerFactory.getName())

View File

@ -1,4 +1,4 @@
package datadog.trace.agent.test;
package datadog.trace.agent.tooling;
/** Used by {@link HelperInjectionTest} */
class HelperClass {}

View File

@ -1,7 +1,5 @@
package datadog.trace.agent.test
package datadog.trace.agent.tooling
import datadog.trace.agent.tooling.HelperInjector
import datadog.trace.agent.tooling.Utils
import spock.lang.Specification
import java.lang.reflect.Method

View File

@ -40,6 +40,7 @@ dependencies {
testCompile group: 'org.objenesis', name: 'objenesis', version: '2.6'
testCompile group: 'cglib', name: 'cglib-nodep', version: '3.2.5'
testCompile 'com.github.stefanbirkner:system-rules:1.17.1'
}
jmh {

View File

@ -6,56 +6,23 @@ import datadog.trace.common.sampling.AllSampler
import datadog.trace.common.writer.DDAgentWriter
import datadog.trace.common.writer.ListWriter
import datadog.trace.common.writer.LoggingWriter
import org.junit.Rule
import org.junit.contrib.java.lang.system.EnvironmentVariables
import org.junit.contrib.java.lang.system.RestoreSystemProperties
import spock.lang.Specification
import spock.lang.Unroll
import java.lang.reflect.Field
import java.lang.reflect.Modifier
import static datadog.trace.common.DDTraceConfig.*
class DDTraceConfigTest extends Specification {
static originalEnvMap
static overrideEnvMap = new HashMap<String, String>()
def setupSpec() {
def envMapField = ProcessEnvironment.getDeclaredField("theUnmodifiableEnvironment")
envMapField.setAccessible(true)
Field modifiersField = Field.getDeclaredField("modifiers")
modifiersField.setAccessible(true)
modifiersField.setInt(envMapField, envMapField.getModifiers() & ~Modifier.FINAL)
originalEnvMap = envMapField.get(null)
overrideEnvMap.putAll(originalEnvMap)
envMapField.set(null, overrideEnvMap)
}
def cleanupSpec() {
def envMapField = ProcessEnvironment.getDeclaredField("theUnmodifiableEnvironment")
envMapField.setAccessible(true)
Field modifiersField = Field.getDeclaredField("modifiers")
modifiersField.setAccessible(true)
modifiersField.setInt(envMapField, envMapField.getModifiers() & ~Modifier.FINAL)
originalEnvMap = envMapField.get(null)
envMapField.set(null, originalEnvMap)
}
def setup() {
overrideEnvMap.clear()
overrideEnvMap.putAll(originalEnvMap)
System.clearProperty(PREFIX + SERVICE_NAME)
System.clearProperty(PREFIX + WRITER_TYPE)
System.clearProperty(PREFIX + AGENT_HOST)
System.clearProperty(PREFIX + AGENT_PORT)
}
@Rule
public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties()
@Rule
public final EnvironmentVariables environmentVariables = new EnvironmentVariables()
def "verify env override"() {
setup:
overrideEnvMap.put("SOME_RANDOM_ENTRY", "asdf")
environmentVariables.set("SOME_RANDOM_ENTRY", "asdf")
expect:
System.getenv("SOME_RANDOM_ENTRY") == "asdf"
@ -94,8 +61,8 @@ class DDTraceConfigTest extends Specification {
def "specify overrides via env vars"() {
when:
overrideEnvMap.put(propToEnvName(PREFIX + SERVICE_NAME), "still something else")
overrideEnvMap.put(propToEnvName(PREFIX + WRITER_TYPE), LoggingWriter.simpleName)
environmentVariables.set(propToEnvName(PREFIX + SERVICE_NAME), "still something else")
environmentVariables.set(propToEnvName(PREFIX + WRITER_TYPE), LoggingWriter.simpleName)
def tracer = new DDTracer()
then:
@ -105,8 +72,8 @@ class DDTraceConfigTest extends Specification {
def "sys props override env vars"() {
when:
overrideEnvMap.put(propToEnvName(PREFIX + SERVICE_NAME), "still something else")
overrideEnvMap.put(propToEnvName(PREFIX + WRITER_TYPE), ListWriter.simpleName)
environmentVariables.set(propToEnvName(PREFIX + SERVICE_NAME), "still something else")
environmentVariables.set(propToEnvName(PREFIX + WRITER_TYPE), ListWriter.simpleName)
System.setProperty(PREFIX + SERVICE_NAME, "what we actually want")
System.setProperty(PREFIX + WRITER_TYPE, DDAgentWriter.simpleName)

View File

@ -43,6 +43,7 @@ dependencies {
testCompile deps.groovy
testCompile deps.testLogging
testCompile group: 'io.ratpack', name: 'ratpack-groovy-test', version: '1.4.6'
testCompile group: 'com.github.stefanbirkner', name: 'system-rules', version: '1.17.1'
}
tasks.withType(Javadoc) {