Ensure rules are loaded on the right classpath for Spring Boot
MODIFY
dd-java-agent/dd-java-agent.gradle
dd-java-agent/dd-java-agent.gradle
@@ -37,7 +37,6 @@ dependencies {
exclude(group: 'org.mongodb', module: 'mongodb-driver-async')
exclude(group: 'org.mongodb', module: 'mongo-java-driver')
}
- compile group: 'io.opentracing.contrib', name: 'opentracing-jdbc', version: '0.0.2'
compile(group: 'io.opentracing.contrib', name: 'opentracing-okhttp3', version: '0.0.5') {
exclude(group: 'com.squareup.okhttp3', module: 'okhttp')
}
@@ -69,6 +68,9 @@ dependencies {
compileOnly group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.119'
compileOnly group: 'com.datastax.cassandra', name: 'cassandra-driver-core', version: '3.2.0'
compileOnly group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'
+
+ // Not bundled in with the agent. Usage requires being on the app's classpath (eg. Spring Boot's executable jar)
+ compileOnly group: 'io.opentracing.contrib', name: 'opentracing-jdbc', version: '0.0.3'
}
jar {

MODIFY
dd-java-agent/src/main/java/com/datadoghq/trace/agent/InstrumentationChecker.java
dd-java-agent/src/main/java/com/datadoghq/trace/agent/InstrumentationChecker.java
@@ -180,6 +180,10 @@ public class InstrumentationChecker {
}
private boolean isClassPresent(final String identifyingPresentClass) {
+ return isClassPresent(identifyingPresentClass, classLoader);
+ }
+
+ static boolean isClassPresent(final String identifyingPresentClass, ClassLoader classLoader) {
try {
return identifyingPresentClass != null
&& Class.forName(identifyingPresentClass, false, classLoader) != null;

MODIFY
dd-java-agent/src/main/java/com/datadoghq/trace/agent/TraceAnnotationsManager.java
dd-java-agent/src/main/java/com/datadoghq/trace/agent/TraceAnnotationsManager.java
@@ -56,17 +56,18 @@ public class TraceAnnotationsManager {
+ "span.setTag(io.opentracing.tag.Tags.ERROR.getKey(),\"true\");\n"
+ "span.deactivate();\n";
private static Retransformer transformer;
+ private static AgentTracerConfig agentTracerConfig;
/**
* This method initializes the manager.
*
* @param trans The ByteMan retransformer
*/
- public static void initialize(final Retransformer trans) throws Exception {
+ public static void initialize(final Retransformer trans) {
log.debug("Initializing {}", TraceAnnotationsManager.class.getSimpleName());
transformer = trans;
//Load configuration
- final AgentTracerConfig agentTracerConfig =
+ agentTracerConfig =
FactoryUtils.loadConfigFromFilePropertyOrResource(
DDTracerFactory.SYSTEM_PROPERTY_CONFIG_PATH,
DDTracerFactory.CONFIG_PATH,
@@ -74,7 +75,25 @@ public class TraceAnnotationsManager {
log.debug("Configuration: {}", agentTracerConfig.toString());
- final List<String> loadedScripts = loadRules(ClassLoader.getSystemClassLoader());
+ if (InstrumentationChecker.isClassPresent(
+ "org.springframework.boot.loader.LaunchedURLClassLoader",
+ ClassLoader.getSystemClassLoader())) {
+ log.info(
+ "Running in the context of a Spring Boot executable jar. Deferring rule loading to run in the LaunchedURLClassLoader.");
+ loadRules("spring-boot-rule.btm", ClassLoader.getSystemClassLoader());
+ } else {
+ finishInitialization(ClassLoader.getSystemClassLoader());
+ }
+ }
+
+ /**
+ * This method is separated out from initialize to allow Spring Boot's LaunchedURLClassLoader to
+ * call it once it is loaded.
+ *
+ * @param classLoader
+ */
+ public static void finishInitialization(ClassLoader classLoader) {
+ final List<String> loadedScripts = loadRules(AGENT_RULES, classLoader);
//Check if some rules have to be uninstalled
final List<String> uninstallScripts =
@@ -85,7 +104,12 @@ public class TraceAnnotationsManager {
uninstallScripts.addAll(disabledInstrumentations);
}
}
+
+ try {
uninstallScripts(loadedScripts, uninstallScripts);
+ } catch (Exception e) {
+ log.warn("Error uninstalling scripts", e);
+ }
//Check if annotations are enabled
if (agentTracerConfig != null
@@ -130,10 +154,13 @@ public class TraceAnnotationsManager {
*
* @param classLoader The classloader
*/
- public static List<String> loadRules(final ClassLoader classLoader) {
+ public static List<String> loadRules(String rulesFileName, final ClassLoader classLoader) {
final List<String> scripts = new ArrayList<>();
if (transformer == null) {
- log.warn("Attempt to load OpenTracing agent rules before transformer initialized");
+ log.warn(
+ "Attempt to load rules file {} on classloader {} before transformer initialized",
+ rulesFileName,
+ classLoader == null ? "bootstrap" : classLoader);
return scripts;
}
@@ -143,7 +170,7 @@ public class TraceAnnotationsManager {
// Load default and custom rules
try {
- final Enumeration<URL> iter = classLoader.getResources(AGENT_RULES);
+ final Enumeration<URL> iter = classLoader.getResources(rulesFileName);
while (iter.hasMoreElements()) {
loadRules(iter.nextElement().toURI(), scriptNames, scripts);
}
@@ -158,10 +185,10 @@ public class TraceAnnotationsManager {
}
log.debug(sw.toString());
} catch (IOException | URISyntaxException e) {
- log.warn("Failed to load OpenTracing agent rules", e);
+ log.warn("Failed to load rules", e);
}
- log.debug("OpenTracing Agent rules loaded");
+ log.debug("Rules loaded from {} on classloader {}", rulesFileName, classLoader);
if (log.isTraceEnabled()) {
for (final String rule : scripts) {
log.trace("Loading rule: {}", rule);

MODIFY
dd-java-agent/src/main/resources/otarules.btm
dd-java-agent/src/main/resources/otarules.btm
@@ -128,10 +128,10 @@ ENDRULE
RULE opentracing-web-servlet-filter_tomcat
CLASS org.apache.catalina.core.ApplicationContext
METHOD <init>
+COMPILE
HELPER com.datadoghq.trace.agent.integration.TomcatServletHelper
AT EXIT
IF TRUE
DO
patch($this)
ENDRULE
-

ADD
dd-java-agent/src/main/resources/spring-boot-rule.btm
+# This rule is required to force the loading of instrumentation rules in otarules.btm when the Spring Boot classloader
+# is first instantiated, before any of the application components have been loaded. Scanning
+# the classloaders resources for OpenTracing agent rule files is far easier this way, then
+# having to scan the nested jar files.
+
+RULE Load rules via Spring Boot Classloader
+CLASS org.springframework.boot.loader.LaunchedURLClassLoader
+METHOD <init>
+AT EXIT
+IF TRUE
+DO
+ com.datadoghq.trace.agent.TraceAnnotationsManager.finishInitialization($0);
+ENDRULE

MODIFY
dd-trace-examples/spring-boot-jdbc/README.md
dd-trace-examples/spring-boot-jdbc/README.md
@@ -55,6 +55,6 @@ The Java Agent embeds the [OpenTracing Java Agent](https://github.com/opentracin
#### Note for JDBC tracing configuration
-JDBC is not automatically instrumented by the Java Agent, so we changed the `application.properties`
-[file](src/main/resources/application.properties) to use the OpenTracing Driver. Without this step in your
-applications, the JDBC driver will not be instrumented.
+[JDBC is not automatically instrumented by the Java Agent](../../README.md#jdbc), so we changed the `application.properties`
+[file](src/main/resources/application.properties) to use the OpenTracing Driver and included it as a dependency in `spring-boot-jdbc.gradle`.
+Without these steps in your applications, the TracingDriver will not work.

MODIFY
dd-trace-examples/spring-boot-jdbc/spring-boot-jdbc.gradle
dd-trace-examples/spring-boot-jdbc/spring-boot-jdbc.gradle
@@ -9,6 +9,7 @@ version = 'demo'
description = 'spring-boot-jdbc'
dependencies {
+ compile group: 'io.opentracing.contrib', name: 'opentracing-jdbc', version: '0.0.3'
compile group: 'com.h2database', name: 'h2', version: '1.4.196'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '1.5.4.RELEASE'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa', version: '1.5.4.RELEASE'

Ensure rules are loaded on the right classpath for Spring Boot