diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java index 2b2eec5f5d..4cfd50a828 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java @@ -16,6 +16,7 @@ import org.slf4j.LoggerFactory; *
The intention is for this class to be loaded by bootstrap classloader to make sure we have * unimpeded access to the rest of Datadog's agent parts. */ +// We cannot use lombok here because we need to configure logger first public class Agent { private static final String SIMPLE_LOGGER_SHOW_DATE_TIME_PROPERTY = @@ -28,19 +29,19 @@ public class Agent { "datadog.slf4j.simpleLogger.defaultLogLevel"; // We cannot use lombok here because we need to configure logger first - private static final Logger LOGGER; + private static final Logger log; static { // We can configure logger here because datadog.trace.agent.AgentBootstrap doesn't touch it. configureLogger(); - LOGGER = LoggerFactory.getLogger(Agent.class); + log = LoggerFactory.getLogger(Agent.class); } // fields must be managed under class lock private static ClassLoader AGENT_CLASSLOADER = null; private static ClassLoader JMXFETCH_CLASSLOADER = null; - public static void start(final Instrumentation inst, final URL bootstrapURL) throws Exception { + public static void start(final Instrumentation inst, final URL bootstrapURL) { startDatadogAgent(inst, bootstrapURL); final boolean appUsingCustomLogManager = isAppUsingCustomLogManager(); @@ -59,10 +60,10 @@ public class Agent { * the global log manager and jmxfetch won't be able to touch it due to classloader locking. */ if (appUsingCustomLogManager) { - LOGGER.debug("Custom logger detected. Delaying JMXFetch initialization."); - registerLogManagerCallback(new StartJmxFetchCallback(inst, bootstrapURL)); + log.debug("Custom logger detected. Delaying JMXFetch initialization."); + registerLogManagerCallback(new StartJmxFetchCallback(bootstrapURL)); } else { - startJmxFetch(inst, bootstrapURL); + startJmxFetch(bootstrapURL); } /* @@ -71,28 +72,30 @@ public class Agent { * logging facility. */ if (isJavaBefore9WithJFR() && appUsingCustomLogManager) { - LOGGER.debug("Custom logger detected. Delaying Datadog Tracer initialization."); - registerLogManagerCallback(new InstallDatadogTracerCallback(inst, bootstrapURL)); + log.debug("Custom logger detected. Delaying Datadog Tracer initialization."); + registerLogManagerCallback(new InstallDatadogTracerCallback(bootstrapURL)); } else { - installDatadogTracer(inst, bootstrapURL); + installDatadogTracer(); } } - private static void registerLogManagerCallback(final Runnable callback) throws Exception { - final Class> agentInstallerClass = - AGENT_CLASSLOADER.loadClass("datadog.trace.agent.tooling.AgentInstaller"); - final Method registerCallbackMethod = - agentInstallerClass.getMethod("registerClassLoadCallback", String.class, Runnable.class); - registerCallbackMethod.invoke(null, "java.util.logging.LogManager", callback); + private static void registerLogManagerCallback(final ClassLoadCallBack callback) { + try { + final Class> agentInstallerClass = + AGENT_CLASSLOADER.loadClass("datadog.trace.agent.tooling.AgentInstaller"); + final Method registerCallbackMethod = + agentInstallerClass.getMethod("registerClassLoadCallback", String.class, Runnable.class); + registerCallbackMethod.invoke(null, "java.util.logging.LogManager", callback); + } catch (final Exception ex) { + log.error("Error registering callback for " + callback.getName(), ex); + } } protected abstract static class ClassLoadCallBack implements Runnable { - final Instrumentation inst; final URL bootstrapURL; - ClassLoadCallBack(final Instrumentation inst, final URL bootstrapURL) { - this.inst = inst; + ClassLoadCallBack(final URL bootstrapURL) { this.bootstrapURL = bootstrapURL; } @@ -111,7 +114,7 @@ public class Agent { try { execute(); } catch (final Exception e) { - LOGGER.error("Failed to run class loader callback {}", getName(), e); + log.error("Failed to run class loader callback {}", getName(), e); } } }); @@ -122,12 +125,12 @@ public class Agent { public abstract String getName(); - public abstract void execute() throws Exception; + public abstract void execute(); } protected static class StartJmxFetchCallback extends ClassLoadCallBack { - StartJmxFetchCallback(final Instrumentation inst, final URL bootstrapURL) { - super(inst, bootstrapURL); + StartJmxFetchCallback(final URL bootstrapURL) { + super(bootstrapURL); } @Override @@ -136,14 +139,14 @@ public class Agent { } @Override - public void execute() throws Exception { - startJmxFetch(inst, bootstrapURL); + public void execute() { + startJmxFetch(bootstrapURL); } } protected static class InstallDatadogTracerCallback extends ClassLoadCallBack { - InstallDatadogTracerCallback(final Instrumentation inst, final URL bootstrapURL) { - super(inst, bootstrapURL); + InstallDatadogTracerCallback(final URL bootstrapURL) { + super(bootstrapURL); } @Override @@ -152,18 +155,18 @@ public class Agent { } @Override - public void execute() throws Exception { - installDatadogTracer(inst, bootstrapURL); + public void execute() { + installDatadogTracer(); } } private static synchronized void startDatadogAgent( - final Instrumentation inst, final URL bootstrapURL) throws Exception { + final Instrumentation inst, final URL bootstrapURL) { if (AGENT_CLASSLOADER == null) { - final ClassLoader agentClassLoader = - createDatadogClassLoader("agent-tooling-and-instrumentation.isolated", bootstrapURL); final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); try { + final ClassLoader agentClassLoader = + createDatadogClassLoader("agent-tooling-and-instrumentation.isolated", bootstrapURL); Thread.currentThread().setContextClassLoader(agentClassLoader); final Class> agentInstallerClass = agentClassLoader.loadClass("datadog.trace.agent.tooling.AgentInstaller"); @@ -171,14 +174,15 @@ public class Agent { agentInstallerClass.getMethod("installBytebuddyAgent", Instrumentation.class); agentInstallerMethod.invoke(null, inst); AGENT_CLASSLOADER = agentClassLoader; + } catch (final Throwable ex) { + log.error("Throwable thrown while installing the Datadog Agent", ex); } finally { Thread.currentThread().setContextClassLoader(contextLoader); } } } - private static synchronized void installDatadogTracer( - final Instrumentation inst, final URL bootstrapURL) throws Exception { + private static synchronized void installDatadogTracer() { if (AGENT_CLASSLOADER == null) { throw new IllegalStateException("Datadog agent should have been started already"); } @@ -194,24 +198,27 @@ public class Agent { tracerInstallerMethod.invoke(null); final Method logVersionInfoMethod = tracerInstallerClass.getMethod("logVersionInfo"); logVersionInfoMethod.invoke(null); + } catch (final Throwable ex) { + log.error("Throwable thrown while installing the Datadog Tracer", ex); } finally { Thread.currentThread().setContextClassLoader(contextLoader); } } - private static synchronized void startJmxFetch(final Instrumentation inst, final URL bootstrapURL) - throws Exception { + private static synchronized void startJmxFetch(final URL bootstrapURL) { if (JMXFETCH_CLASSLOADER == null) { - final ClassLoader jmxFetchClassLoader = - createDatadogClassLoader("agent-jmxfetch.isolated", bootstrapURL); final ClassLoader contextLoader = Thread.currentThread().getContextClassLoader(); try { + final ClassLoader jmxFetchClassLoader = + createDatadogClassLoader("agent-jmxfetch.isolated", bootstrapURL); Thread.currentThread().setContextClassLoader(jmxFetchClassLoader); final Class> jmxFetchAgentClass = jmxFetchClassLoader.loadClass("datadog.trace.agent.jmxfetch.JMXFetch"); final Method jmxFetchInstallerMethod = jmxFetchAgentClass.getMethod("run"); jmxFetchInstallerMethod.invoke(null); JMXFETCH_CLASSLOADER = jmxFetchClassLoader; + } catch (final Throwable ex) { + log.error("Throwable thrown while starting JmxFetch", ex); } finally { Thread.currentThread().setContextClassLoader(contextLoader); } @@ -305,8 +312,8 @@ public class Agent { System.getenv(tracerCustomLogManSysprop.replace('.', '_').toUpperCase()); if (customLogManagerProp != null || customLogManagerEnv != null) { - LOGGER.debug("Prop - customlogmanager: " + customLogManagerProp); - LOGGER.debug("Env - customlogmanager: " + customLogManagerEnv); + log.debug("Prop - customlogmanager: " + customLogManagerProp); + log.debug("Env - customlogmanager: " + customLogManagerEnv); // Allow setting to skip these automatic checks: return Boolean.parseBoolean(customLogManagerProp) || Boolean.parseBoolean(customLogManagerEnv); @@ -314,7 +321,7 @@ public class Agent { final String jbossHome = System.getenv("JBOSS_HOME"); if (jbossHome != null) { - LOGGER.debug("Env - jboss: " + jbossHome); + log.debug("Env - jboss: " + jbossHome); // JBoss/Wildfly is known to set a custom log manager after startup. // Originally we were checking for the presence of a jboss class, // but it seems some non-jboss applications have jboss classes on the classpath. @@ -327,8 +334,8 @@ public class Agent { if (logManagerProp != null) { final boolean onSysClasspath = ClassLoader.getSystemResource(logManagerProp.replaceAll("\\.", "/") + ".class") != null; - LOGGER.debug("Prop - logging.manager: " + logManagerProp); - LOGGER.debug("logging.manager on system classpath: " + onSysClasspath); + log.debug("Prop - logging.manager: " + logManagerProp); + log.debug("logging.manager on system classpath: " + onSysClasspath); // Some applications set java.util.logging.manager but never actually initialize the logger. // Check to see if the configured manager is on the system classpath. // If so, it should be safe to initialize jmxfetch which will setup the log manager. diff --git a/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java b/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java index ed19b12962..9d12306fa5 100644 --- a/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java +++ b/dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentBootstrap.java @@ -41,19 +41,23 @@ import java.util.regex.Pattern; */ public class AgentBootstrap { - public static void premain(final String agentArgs, final Instrumentation inst) throws Exception { + public static void premain(final String agentArgs, final Instrumentation inst) { agentmain(agentArgs, inst); } - public static void agentmain(final String agentArgs, final Instrumentation inst) - throws Exception { + public static void agentmain(final String agentArgs, final Instrumentation inst) { + try { - final URL bootstrapURL = installBootstrapJar(inst); + final URL bootstrapURL = installBootstrapJar(inst); - final Class> agentClass = - ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.Agent"); - final Method startMethod = agentClass.getMethod("start", Instrumentation.class, URL.class); - startMethod.invoke(null, inst, bootstrapURL); + final Class> agentClass = + ClassLoader.getSystemClassLoader().loadClass("datadog.trace.bootstrap.Agent"); + final Method startMethod = agentClass.getMethod("start", Instrumentation.class, URL.class); + startMethod.invoke(null, inst, bootstrapURL); + } catch (final Throwable ex) { + // Don't rethrow. We don't have a log manager here, so just print. + ex.printStackTrace(); + } } private static synchronized URL installBootstrapJar(final Instrumentation inst) @@ -106,8 +110,12 @@ public class AgentBootstrap { throw new RuntimeException("Unable to parse javaagent parameter: " + agentArgument); } - bootstrapURL = new URL("file:" + matcher.group(1)); - inst.appendToBootstrapClassLoaderSearch(new JarFile(new File(bootstrapURL.toURI()))); + final File javaagentFile = new File(matcher.group(1)); + if (!(javaagentFile.exists() || javaagentFile.isFile())) { + throw new RuntimeException("Unable to find javaagent file: " + javaagentFile); + } + bootstrapURL = javaagentFile.toURI().toURL(); + inst.appendToBootstrapClassLoaderSearch(new JarFile(javaagentFile)); return bootstrapURL; } diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy index 5afe435c67..a3d8d48181 100644 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy +++ b/dd-java-agent/src/test/groovy/datadog/trace/agent/AgentLoadedIntoBootstrapTest.groovy @@ -5,7 +5,7 @@ import jvmbootstraptest.AgentLoadedChecker import spock.lang.Specification class AgentLoadedIntoBootstrapTest extends Specification { - + def "Agent loads in when separate jvm is launched"() { expect: IntegrationTestUtils.runOnSeparateJvm(AgentLoadedChecker.getName() diff --git a/dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy b/dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy index 1b70815081..de5ac7369c 100644 --- a/dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy +++ b/dd-java-agent/src/test/groovy/datadog/trace/agent/JMXFetchTest.groovy @@ -2,6 +2,7 @@ package datadog.trace.agent import datadog.trace.agent.test.IntegrationTestUtils import datadog.trace.api.Config +import jvmbootstraptest.AgentLoadedChecker import org.junit.Rule import org.junit.contrib.java.lang.system.RestoreSystemProperties import spock.lang.Specification @@ -49,6 +50,16 @@ class JMXFetchTest extends Specification { Thread.currentThread().setContextClassLoader(currentContextLoader) } + def "Agent loads when JmxFetch is misconfigured"() { + // verify the agent starts up correctly with a bogus address. + expect: + IntegrationTestUtils.runOnSeparateJvm(AgentLoadedChecker.getName() + , ["-Ddd.jmxfetch.enabled=true", "-Ddd.jmxfetch.statsd.host=example.local"] as String[] + , "" as String[] + , [:] + , true) == 0 + } + def "test jmxfetch config"() { setup: names.each {