Merge pull request #741 from DataDog/mar-kolya/tomcat-classloadin-instrumentation
Add instrumentation for Tomcat webapp classloading
This commit is contained in:
commit
3029d3d85c
|
@ -0,0 +1,68 @@
|
|||
package datadog.trace.instrumentation.tomcat;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import datadog.trace.agent.tooling.Constants;
|
||||
import datadog.trace.agent.tooling.Instrumenter;
|
||||
import datadog.trace.api.GlobalTracer;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
/**
|
||||
* Instrument Tomcat's web app classloader so it loads Datadog's bootstrap classes from parent
|
||||
* classloader. Without this change web apps get their oen versions of Datadog's classes leading to
|
||||
* there being multiple {@link GlobalTracer}s in existance, some of them not configured properly.
|
||||
* This is really the same idea we have for OSGi and JBoss.
|
||||
*/
|
||||
@AutoService(Instrumenter.class)
|
||||
public final class TomcatClassloadingInstrumentation extends Instrumenter.Default {
|
||||
public TomcatClassloadingInstrumentation() {
|
||||
super("tomcat-classloading");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return safeHasSuperType(named("org.apache.catalina.loader.WebappClassLoaderBase"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] helperClassNames() {
|
||||
return new String[] {Constants.class.getName()};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isMethod()
|
||||
.and(named("filter"))
|
||||
.and(takesArgument(0, String.class))
|
||||
// Older versions have 1 argument method, newer versions have two arguments
|
||||
.and(takesArguments(2).or(takesArguments(1))),
|
||||
WebappClassLoaderAdvice.class.getName());
|
||||
}
|
||||
|
||||
public static class WebappClassLoaderAdvice {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void methodExit(
|
||||
@Advice.Argument(0) final String name, @Advice.Return(readOnly = false) boolean result) {
|
||||
if (result) {
|
||||
return;
|
||||
}
|
||||
for (final String prefix : Constants.BOOTSTRAP_PACKAGE_PREFIXES) {
|
||||
if (name.startsWith(prefix)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import datadog.trace.agent.test.AgentTestRunner
|
||||
import datadog.trace.api.GlobalTracer
|
||||
import org.apache.catalina.WebResource
|
||||
import org.apache.catalina.WebResourceRoot
|
||||
import org.apache.catalina.loader.ParallelWebappClassLoader
|
||||
|
||||
class TomcatClassloadingTest extends AgentTestRunner {
|
||||
|
||||
WebResourceRoot resources = Mock(WebResourceRoot) {
|
||||
getResource(_) >> Mock(WebResource)
|
||||
listResources(_) >> []
|
||||
// Looks like 9.x.x needs this one:
|
||||
getResources(_) >> []
|
||||
}
|
||||
ParallelWebappClassLoader classloader = new ParallelWebappClassLoader(null)
|
||||
|
||||
def "tomcat class loading delegates to parent for Datadog classes"() {
|
||||
setup:
|
||||
classloader.setResources(resources)
|
||||
classloader.init()
|
||||
classloader.start()
|
||||
|
||||
when:
|
||||
// If instrumentation didn't work this would blow up with NPE due to incomplete resources mocking
|
||||
def clazz = classloader.loadClass("datadog.trace.api.GlobalTracer")
|
||||
|
||||
then:
|
||||
clazz == GlobalTracer
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
muzzle {
|
||||
pass {
|
||||
group = "org.apache.tomcat"
|
||||
module = 'tomcat-catalina'
|
||||
versions = "[3.0.14,)"
|
||||
assertInverse = true
|
||||
}
|
||||
}
|
||||
|
||||
apply from: "${rootDir}/gradle/java.gradle"
|
||||
|
||||
apply plugin: 'org.unbroken-dome.test-sets'
|
||||
|
||||
testSets {
|
||||
latestDepTest {
|
||||
dirName = 'test'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':dd-java-agent:agent-tooling')
|
||||
compile deps.bytebuddy
|
||||
annotationProcessor deps.autoservice
|
||||
implementation deps.autoservice
|
||||
|
||||
testCompile project(':dd-java-agent:testing')
|
||||
|
||||
//This seems to be the earliest version that has org.apache.catalina.loader.WebappClassLoaderBase
|
||||
//Older versions would require slightly different instrumentation.
|
||||
testCompile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '8.0.14'
|
||||
|
||||
latestDepTestCompile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '+'
|
||||
}
|
|
@ -77,6 +77,7 @@ include ':dd-java-agent:instrumentation:sparkjava-2.3'
|
|||
include ':dd-java-agent:instrumentation:spring-web'
|
||||
include ':dd-java-agent:instrumentation:spring-webflux'
|
||||
include ':dd-java-agent:instrumentation:spymemcached-2.12'
|
||||
include ':dd-java-agent:instrumentation:tomcat-classloading'
|
||||
include ':dd-java-agent:instrumentation:trace-annotation'
|
||||
include ':dd-java-agent:instrumentation:vertx'
|
||||
|
||||
|
|
Loading…
Reference in New Issue