From 7d28a32fba4fe3dd6f6a6070ba7301a67cb8bbdc Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 6 Feb 2018 10:46:03 +1000 Subject: [PATCH] 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. --- dd-java-agent/dd-java-agent.gradle | 6 +- .../ApacheHttpClientInstrumentation.java | 8 +- .../aws/AWSClientInstrumentation.java | 8 +- .../CassandraClientInstrumentation.java | 9 +- .../jdbc/ConnectionInstrumentation.java | 8 +- .../PreparedStatementInstrumentation.java | 8 +- .../jdbc/StatementInstrumentation.java | 8 +- .../JMS1MessageConsumerInstrumentation.java | 8 +- .../JMS1MessageListenerInstrumentation.java | 8 +- .../JMS1MessageProducerInstrumentation.java | 8 +- .../JMS2MessageConsumerInstrumentation.java | 8 +- .../JMS2MessageListenerInstrumentation.java | 8 +- .../JMS2MessageProducerInstrumentation.java | 8 +- .../KafkaConsumerInstrumentation.java | 13 +- .../KafkaProducerInstrumentation.java | 13 +- ...afkaTest.groovy => KafkaClientTest.groovy} | 3 +- .../KafkaStreamsProcessorInstrumentation.java | 28 ++- ...NodeRecordDeserializerInstrumentation.java | 13 +- .../src/test/groovy/KafkaStreamsTest.groovy | 1 + .../mongo/MongoClientInstrumentation.java | 8 +- .../MongoAsyncClientInstrumentation.java | 8 +- .../okhttp3/OkHttp3Instrumentation.java | 8 +- .../servlet2/FilterChain2Instrumentation.java | 8 +- .../servlet2/HttpServlet2Instrumentation.java | 8 +- .../servlet3/FilterChain3Instrumentation.java | 8 +- .../servlet3/HttpServlet3Instrumentation.java | 8 +- .../springweb/SpringWebInstrumentation.java | 8 +- .../TraceAnnotationInstrumentation.java | 8 +- .../trace/agent/tooling/AgentInstaller.java | 2 +- .../trace/agent/tooling/Instrumenter.java | 49 +++++ .../datadog/trace/agent/tooling/Utils.java | 7 + .../agent/{test => tooling}/BadAdvice.java | 2 +- .../ConfigurableInstrumenterTest.groovy | 178 ++++++++++++++++++ .../ExceptionHandlerTest.groovy | 27 ++- .../agent/{test => tooling}/HelperClass.java | 2 +- .../HelperInjectionTest.groovy | 4 +- dd-trace-ot/dd-trace-ot.gradle | 1 + .../datadog/trace/DDTraceConfigTest.groovy | 57 ++---- gradle/java.gradle | 1 + 39 files changed, 452 insertions(+), 124 deletions(-) rename dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/{KafkaTest.groovy => KafkaClientTest.groovy} (97%) rename dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/{test => tooling}/BadAdvice.java (88%) create mode 100644 dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/ConfigurableInstrumenterTest.groovy rename dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/{test => tooling}/ExceptionHandlerTest.groovy (87%) rename dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/{test => tooling}/HelperClass.java (63%) rename dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/{test => tooling}/HelperInjectionTest.groovy (92%) diff --git a/dd-java-agent/dd-java-agent.gradle b/dd-java-agent/dd-java-agent.gradle index 51485852d4..575748b296 100644 --- a/dd-java-agent/dd-java-agent.gradle +++ b/dd-java-agent/dd-java-agent.gradle @@ -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())) } } } diff --git a/dd-java-agent/instrumentation/apache-httpclient-4.3/src/main/java/datadog/trace/instrumentation/apachehttpclient/ApacheHttpClientInstrumentation.java b/dd-java-agent/instrumentation/apache-httpclient-4.3/src/main/java/datadog/trace/instrumentation/apachehttpclient/ApacheHttpClientInstrumentation.java index 705a1b8bba..e828d4e1d0 100644 --- a/dd-java-agent/instrumentation/apache-httpclient-4.3/src/main/java/datadog/trace/instrumentation/apachehttpclient/ApacheHttpClientInstrumentation.java +++ b/dd-java-agent/instrumentation/apache-httpclient-4.3/src/main/java/datadog/trace/instrumentation/apachehttpclient/ApacheHttpClientInstrumentation.java @@ -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"), diff --git a/dd-java-agent/instrumentation/aws-sdk/src/main/java/datadog/trace/instrumentation/aws/AWSClientInstrumentation.java b/dd-java-agent/instrumentation/aws-sdk/src/main/java/datadog/trace/instrumentation/aws/AWSClientInstrumentation.java index 6df87b05be..4dc112a47e 100644 --- a/dd-java-agent/instrumentation/aws-sdk/src/main/java/datadog/trace/instrumentation/aws/AWSClientInstrumentation.java +++ b/dd-java-agent/instrumentation/aws-sdk/src/main/java/datadog/trace/instrumentation/aws/AWSClientInstrumentation.java @@ -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")), diff --git a/dd-java-agent/instrumentation/datastax-cassandra-3.2/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientInstrumentation.java b/dd-java-agent/instrumentation/datastax-cassandra-3.2/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientInstrumentation.java index f500ef89a1..75798bcc58 100644 --- a/dd-java-agent/instrumentation/datastax-cassandra-3.2/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientInstrumentation.java +++ b/dd-java-agent/instrumentation/datastax-cassandra-3.2/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientInstrumentation.java @@ -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"), diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/ConnectionInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/ConnectionInstrumentation.java index 110a257d5f..ed214096bc 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/ConnectionInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/ConnectionInstrumentation.java @@ -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 connectionInfo = Collections.synchronizedMap(new WeakHashMap()); public static final Map preparedStatements = Collections.synchronizedMap(new WeakHashMap()); + 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( diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java index c0444590a1..b9eee9e754 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java @@ -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( diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java index 471fc346b0..9657c6b338 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java @@ -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( diff --git a/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageConsumerInstrumentation.java b/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageConsumerInstrumentation.java index 3c7d180f03..9b19bd1cf4 100644 --- a/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageConsumerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageConsumerInstrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageListenerInstrumentation.java b/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageListenerInstrumentation.java index dba04f121b..b69153d7d6 100644 --- a/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageListenerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageListenerInstrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageProducerInstrumentation.java b/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageProducerInstrumentation.java index 11febaca04..d66e9ba032 100644 --- a/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms-1/src/main/java/datadog/trace/instrumentation/jms1/JMS1MessageProducerInstrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageConsumerInstrumentation.java b/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageConsumerInstrumentation.java index fa06ae82d1..8c1abf0880 100644 --- a/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageConsumerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageConsumerInstrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageListenerInstrumentation.java b/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageListenerInstrumentation.java index ab9d7c4b2a..36b0e9f526 100644 --- a/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageListenerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageListenerInstrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageProducerInstrumentation.java b/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageProducerInstrumentation.java index 60adc53c4f..9ae0956a20 100644 --- a/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/jms-2/src/main/java/datadog/trace/instrumentation/jms2/JMS2MessageProducerInstrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaConsumerInstrumentation.java b/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaConsumerInstrumentation.java index 2c5320111d..0a22b25a2d 100644 --- a/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaConsumerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaConsumerInstrumentation.java @@ -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"), diff --git a/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java b/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java index a8c33bb652..b242c07000 100644 --- a/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka-clients-0.11/src/main/java/datadog/trace/instrumentation/kafka_clients/KafkaProducerInstrumentation.java @@ -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"), diff --git a/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/KafkaTest.groovy b/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/KafkaClientTest.groovy similarity index 97% rename from dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/KafkaTest.groovy rename to dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/KafkaClientTest.groovy index 08cec690da..b36de45d09 100644 --- a/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/KafkaTest.groovy +++ b/dd-java-agent/instrumentation/kafka-clients-0.11/src/test/groovy/KafkaClientTest.groovy @@ -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 diff --git a/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsProcessorInstrumentation.java b/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsProcessorInstrumentation.java index e30f81ef26..1ffc519eaa 100644 --- a/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsProcessorInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsProcessorInstrumentation.java @@ -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"), diff --git a/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsSourceNodeRecordDeserializerInstrumentation.java b/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsSourceNodeRecordDeserializerInstrumentation.java index 4e64ddb6b5..1dc66b10c3 100644 --- a/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsSourceNodeRecordDeserializerInstrumentation.java +++ b/dd-java-agent/instrumentation/kafka-streams-0.11/src/main/java/datadog/trace/instrumentation/kafka_streams/KafkaStreamsSourceNodeRecordDeserializerInstrumentation.java @@ -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"), diff --git a/dd-java-agent/instrumentation/kafka-streams-0.11/src/test/groovy/KafkaStreamsTest.groovy b/dd-java-agent/instrumentation/kafka-streams-0.11/src/test/groovy/KafkaStreamsTest.groovy index 002868d811..3e71e50edd 100644 --- a/dd-java-agent/instrumentation/kafka-streams-0.11/src/test/groovy/KafkaStreamsTest.groovy +++ b/dd-java-agent/instrumentation/kafka-streams-0.11/src/test/groovy/KafkaStreamsTest.groovy @@ -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 diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java b/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java index db6c8c0234..9744d822e2 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java +++ b/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java @@ -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") diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java b/dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java index 72439d6d03..4cbaf795a5 100644 --- a/dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java +++ b/dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java @@ -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") diff --git a/dd-java-agent/instrumentation/okhttp-3/src/main/java/datadog/trace/instrumentation/okhttp3/OkHttp3Instrumentation.java b/dd-java-agent/instrumentation/okhttp-3/src/main/java/datadog/trace/instrumentation/okhttp3/OkHttp3Instrumentation.java index 742960a137..6f51866242 100644 --- a/dd-java-agent/instrumentation/okhttp-3/src/main/java/datadog/trace/instrumentation/okhttp3/OkHttp3Instrumentation.java +++ b/dd-java-agent/instrumentation/okhttp-3/src/main/java/datadog/trace/instrumentation/okhttp3/OkHttp3Instrumentation.java @@ -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"), diff --git a/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/FilterChain2Instrumentation.java b/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/FilterChain2Instrumentation.java index 84910662ad..944501151d 100644 --- a/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/FilterChain2Instrumentation.java +++ b/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/FilterChain2Instrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/HttpServlet2Instrumentation.java b/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/HttpServlet2Instrumentation.java index 66a05282a0..05c1779471 100644 --- a/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/HttpServlet2Instrumentation.java +++ b/dd-java-agent/instrumentation/servlet-2/src/main/java/datadog/trace/instrumentation/servlet2/HttpServlet2Instrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/FilterChain3Instrumentation.java b/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/FilterChain3Instrumentation.java index 962a0c70ae..0c4beecd20 100644 --- a/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/FilterChain3Instrumentation.java +++ b/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/FilterChain3Instrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/HttpServlet3Instrumentation.java b/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/HttpServlet3Instrumentation.java index f53e55eb40..7e0612cb66 100644 --- a/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/HttpServlet3Instrumentation.java +++ b/dd-java-agent/instrumentation/servlet-3/src/main/java/datadog/trace/instrumentation/servlet3/HttpServlet3Instrumentation.java @@ -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"))), diff --git a/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java b/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java index c4af9bffa7..98c2a4a819 100644 --- a/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java +++ b/dd-java-agent/instrumentation/spring-web/src/main/java/datadog/trace/instrumentation/springweb/SpringWebInstrumentation.java @@ -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()) diff --git a/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAnnotationInstrumentation.java b/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAnnotationInstrumentation.java index e11be63d78..1173376b0b 100644 --- a/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAnnotationInstrumentation.java +++ b/dd-java-agent/instrumentation/trace-annotation/src/main/java/datadog/trace/instrumentation/trace_annotation/TraceAnnotationInstrumentation.java @@ -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( diff --git a/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java b/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java index e50dd1bce1..0269f11372 100644 --- a/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java +++ b/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/AgentInstaller.java @@ -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); diff --git a/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java b/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java index 629364a23e..9c4b20f443 100644 --- a/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java +++ b/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java @@ -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 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); + } } diff --git a/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Utils.java b/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Utils.java index ef5b9f60af..4fb1a4ec92 100644 --- a/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Utils.java +++ b/dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Utils.java @@ -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() {} } diff --git a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/BadAdvice.java b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/BadAdvice.java similarity index 88% rename from dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/BadAdvice.java rename to dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/BadAdvice.java index f5b2b824fb..899916ae31 100644 --- a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/BadAdvice.java +++ b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/BadAdvice.java @@ -1,4 +1,4 @@ -package datadog.trace.agent.test; +package datadog.trace.agent.tooling; import net.bytebuddy.asm.Advice; diff --git a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/ConfigurableInstrumenterTest.groovy b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/ConfigurableInstrumenterTest.groovy new file mode 100644 index 0000000000..eba0f860bb --- /dev/null +++ b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/ConfigurableInstrumenterTest.groovy @@ -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 + } + } +} diff --git a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/ExceptionHandlerTest.groovy b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/ExceptionHandlerTest.groovy similarity index 87% rename from dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/ExceptionHandlerTest.groovy rename to dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/ExceptionHandlerTest.groovy index 8f62bf64d9..db5f439bbb 100644 --- a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/ExceptionHandlerTest.groovy +++ b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/ExceptionHandlerTest.groovy @@ -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()) diff --git a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/HelperClass.java b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/HelperClass.java similarity index 63% rename from dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/HelperClass.java rename to dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/HelperClass.java index 91ed6a4271..2e781bb340 100644 --- a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/HelperClass.java +++ b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/HelperClass.java @@ -1,4 +1,4 @@ -package datadog.trace.agent.test; +package datadog.trace.agent.tooling; /** Used by {@link HelperInjectionTest} */ class HelperClass {} diff --git a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/HelperInjectionTest.groovy similarity index 92% rename from dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy rename to dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/HelperInjectionTest.groovy index 8ee475779a..fd9ec5e697 100644 --- a/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/test/HelperInjectionTest.groovy +++ b/dd-java-agent/tooling/src/test/groovy/datadog/trace/agent/tooling/HelperInjectionTest.groovy @@ -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 diff --git a/dd-trace-ot/dd-trace-ot.gradle b/dd-trace-ot/dd-trace-ot.gradle index 137e3ce36f..dde71c3f16 100644 --- a/dd-trace-ot/dd-trace-ot.gradle +++ b/dd-trace-ot/dd-trace-ot.gradle @@ -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 { diff --git a/dd-trace-ot/src/test/groovy/datadog/trace/DDTraceConfigTest.groovy b/dd-trace-ot/src/test/groovy/datadog/trace/DDTraceConfigTest.groovy index d59a6b7634..38869d1dd8 100644 --- a/dd-trace-ot/src/test/groovy/datadog/trace/DDTraceConfigTest.groovy +++ b/dd-trace-ot/src/test/groovy/datadog/trace/DDTraceConfigTest.groovy @@ -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() - - 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) diff --git a/gradle/java.gradle b/gradle/java.gradle index bddd35e2cf..04e789656f 100644 --- a/gradle/java.gradle +++ b/gradle/java.gradle @@ -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) {