Merge branch 'master' into mar-kolya/profiling-lz4
This commit is contained in:
commit
b3d81281f6
|
@ -2,3 +2,6 @@
|
||||||
# https://help.github.com/en/articles/about-code-owners
|
# https://help.github.com/en/articles/about-code-owners
|
||||||
|
|
||||||
* @DataDog/apm-java
|
* @DataDog/apm-java
|
||||||
|
|
||||||
|
dd-java-agent/agent-profiling/ @DataDog/profiling
|
||||||
|
dd-smoke-tests/profiling-integration-tests/ @DataDog/profiling
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
### Contributing
|
## Contributing
|
||||||
|
|
||||||
Pull requests for bug fixes are welcome, but before submitting new features or changes to current functionality [open an issue](https://github.com/DataDog/dd-trace-java/issues/new)
|
Pull requests for bug fixes are welcome, but before submitting new features or changes to current functionality [open an issue](https://github.com/DataDog/dd-trace-java/issues/new)
|
||||||
and discuss your ideas or propose the changes you wish to make. After a resolution is reached a PR can be submitted for review.
|
and discuss your ideas or propose the changes you wish to make. After a resolution is reached a PR can be submitted for review.
|
||||||
|
@ -15,7 +15,14 @@ Java files must be formatted using [google-java-format](https://github.com/googl
|
||||||
|
|
||||||
Other source files (Groovy, Scala, etc) should ideally be formatted by Intellij Idea's default formatting, but are not enforced.
|
Other source files (Groovy, Scala, etc) should ideally be formatted by Intellij Idea's default formatting, but are not enforced.
|
||||||
|
|
||||||
### Intellij Idea
|
### Intellij IDEA
|
||||||
|
|
||||||
|
Compiler settings:
|
||||||
|
* OpenJDK 11 must be installed to build the entire project. Under `SDKs` it must have the name `11`.
|
||||||
|
* Under `Build, Execution, Deployment > Compiler > Java Compiler` disable `Use '--release' option for cross-compilation`
|
||||||
|
|
||||||
|
Required plugins:
|
||||||
|
* [Lombok](https://plugins.jetbrains.com/plugin/6317-lombok-plugin)
|
||||||
|
|
||||||
Suggested plugins and settings:
|
Suggested plugins and settings:
|
||||||
|
|
||||||
|
@ -25,6 +32,5 @@ Suggested plugins and settings:
|
||||||
* With java use the following import layout (groovy should still use the default) to ensure consistency with google-java-format:
|
* With java use the following import layout (groovy should still use the default) to ensure consistency with google-java-format:
|
||||||

|

|
||||||
* [Google Java Format](https://plugins.jetbrains.com/plugin/8527-google-java-format)
|
* [Google Java Format](https://plugins.jetbrains.com/plugin/8527-google-java-format)
|
||||||
* [Lombok](https://plugins.jetbrains.com/plugin/6317-lombok-plugin)
|
|
||||||
* [Save Actions](https://plugins.jetbrains.com/plugin/7642-save-actions)
|
* [Save Actions](https://plugins.jetbrains.com/plugin/7642-save-actions)
|
||||||

|

|
||||||
|
|
|
@ -23,8 +23,6 @@ public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE> extends
|
||||||
|
|
||||||
protected abstract URI url(REQUEST request) throws URISyntaxException;
|
protected abstract URI url(REQUEST request) throws URISyntaxException;
|
||||||
|
|
||||||
protected abstract String peerHostname(CONNECTION connection);
|
|
||||||
|
|
||||||
protected abstract String peerHostIP(CONNECTION connection);
|
protected abstract String peerHostIP(CONNECTION connection);
|
||||||
|
|
||||||
protected abstract Integer peerPort(CONNECTION connection);
|
protected abstract Integer peerPort(CONNECTION connection);
|
||||||
|
@ -87,7 +85,6 @@ public abstract class HttpServerDecorator<REQUEST, CONNECTION, RESPONSE> extends
|
||||||
public AgentSpan onConnection(final AgentSpan span, final CONNECTION connection) {
|
public AgentSpan onConnection(final AgentSpan span, final CONNECTION connection) {
|
||||||
assert span != null;
|
assert span != null;
|
||||||
if (connection != null) {
|
if (connection != null) {
|
||||||
span.setTag(Tags.PEER_HOSTNAME.getKey(), peerHostname(connection));
|
|
||||||
final String ip = peerHostIP(connection);
|
final String ip = peerHostIP(connection);
|
||||||
if (ip != null) {
|
if (ip != null) {
|
||||||
if (VALID_IPV4_ADDRESS.matcher(ip).matches()) {
|
if (VALID_IPV4_ADDRESS.matcher(ip).matches()) {
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ClassLoaderMatcher.skipClassLoader;
|
import static datadog.trace.agent.tooling.ClassLoaderMatcher.skipClassLoader;
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.GlobalIgnoresMatcher.globalIgnoresMatcher;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.any;
|
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.nameMatches;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.none;
|
import static net.bytebuddy.matcher.ElementMatchers.none;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.context.FieldBackedProvider;
|
||||||
import datadog.trace.api.Config;
|
import datadog.trace.api.Config;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -21,10 +19,10 @@ import java.util.ServiceLoader;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
|
import net.bytebuddy.agent.builder.ResettableClassFileTransformer;
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.dynamic.DynamicType;
|
import net.bytebuddy.dynamic.DynamicType;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
import net.bytebuddy.matcher.ElementMatchers;
|
|
||||||
import net.bytebuddy.utility.JavaModule;
|
import net.bytebuddy.utility.JavaModule;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -54,91 +52,33 @@ public class AgentInstaller {
|
||||||
final Instrumentation inst, final AgentBuilder.Listener... listeners) {
|
final Instrumentation inst, final AgentBuilder.Listener... listeners) {
|
||||||
INSTRUMENTATION = inst;
|
INSTRUMENTATION = inst;
|
||||||
|
|
||||||
|
addByteBuddyRawSetting();
|
||||||
|
|
||||||
|
FieldBackedProvider.resetContextMatchers();
|
||||||
|
|
||||||
AgentBuilder agentBuilder =
|
AgentBuilder agentBuilder =
|
||||||
new AgentBuilder.Default()
|
new AgentBuilder.Default()
|
||||||
.disableClassFormatChanges()
|
.disableClassFormatChanges()
|
||||||
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
|
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
|
||||||
.with(new RedefinitionLoggingListener())
|
|
||||||
.with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY)
|
.with(AgentBuilder.DescriptionStrategy.Default.POOL_ONLY)
|
||||||
.with(AgentTooling.poolStrategy())
|
.with(AgentTooling.poolStrategy())
|
||||||
.with(new TransformLoggingListener())
|
|
||||||
.with(new ClassLoadListener())
|
.with(new ClassLoadListener())
|
||||||
.with(AgentTooling.locationStrategy())
|
.with(AgentTooling.locationStrategy())
|
||||||
// FIXME: we cannot enable it yet due to BB/JVM bug, see
|
// FIXME: we cannot enable it yet due to BB/JVM bug, see
|
||||||
// https://github.com/raphw/byte-buddy/issues/558
|
// https://github.com/raphw/byte-buddy/issues/558
|
||||||
// .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED)
|
// .with(AgentBuilder.LambdaInstrumentationStrategy.ENABLED)
|
||||||
.ignore(any(), skipClassLoader())
|
.ignore(any(), skipClassLoader())
|
||||||
// Unlikely to ever need to instrument an annotation:
|
.or(globalIgnoresMatcher())
|
||||||
.or(ElementMatchers.<TypeDescription>isAnnotation())
|
|
||||||
// Unlikely to ever need to instrument an enum:
|
|
||||||
.or(ElementMatchers.<TypeDescription>isEnum())
|
|
||||||
.or(
|
|
||||||
nameStartsWith("datadog.trace.")
|
|
||||||
// FIXME: We should remove this once
|
|
||||||
// https://github.com/raphw/byte-buddy/issues/558 is fixed
|
|
||||||
.and(
|
|
||||||
not(
|
|
||||||
named(
|
|
||||||
"datadog.trace.bootstrap.instrumentation.java.concurrent.RunnableWrapper")
|
|
||||||
.or(
|
|
||||||
named(
|
|
||||||
"datadog.trace.bootstrap.instrumentation.java.concurrent.CallableWrapper")))))
|
|
||||||
.or(nameStartsWith("datadog.opentracing."))
|
|
||||||
.or(nameStartsWith("datadog.slf4j."))
|
|
||||||
.or(nameStartsWith("net.bytebuddy."))
|
|
||||||
.or(
|
|
||||||
nameStartsWith("java.")
|
|
||||||
.and(
|
|
||||||
not(
|
|
||||||
named("java.net.URL")
|
|
||||||
.or(named("java.net.HttpURLConnection"))
|
|
||||||
.or(nameStartsWith("java.rmi."))
|
|
||||||
.or(nameStartsWith("java.util.concurrent."))
|
|
||||||
.or(
|
|
||||||
nameStartsWith("java.util.logging.")
|
|
||||||
// Concurrent instrumentation modifies the strucutre of
|
|
||||||
// Cleaner class incompaibly with java9+ modules.
|
|
||||||
// Working around until a long-term fix for modules can be
|
|
||||||
// put in place.
|
|
||||||
.and(not(named("java.util.logging.LogManager$Cleaner")))))))
|
|
||||||
.or(
|
|
||||||
nameStartsWith("com.sun.")
|
|
||||||
.and(
|
|
||||||
not(
|
|
||||||
nameStartsWith("com.sun.messaging.")
|
|
||||||
.or(nameStartsWith("com.sun.jersey.api.client")))))
|
|
||||||
.or(
|
|
||||||
nameStartsWith("sun.")
|
|
||||||
.and(
|
|
||||||
not(
|
|
||||||
nameStartsWith("sun.net.www.protocol.")
|
|
||||||
.or(nameStartsWith("sun.rmi.server"))
|
|
||||||
.or(nameStartsWith("sun.rmi.transport"))
|
|
||||||
.or(named("sun.net.www.http.HttpClient")))))
|
|
||||||
.or(nameStartsWith("jdk."))
|
|
||||||
.or(nameStartsWith("org.aspectj."))
|
|
||||||
.or(nameStartsWith("org.groovy."))
|
|
||||||
.or(nameStartsWith("org.codehaus.groovy.macro."))
|
|
||||||
.or(nameStartsWith("com.intellij.rt.debugger."))
|
|
||||||
.or(nameStartsWith("com.p6spy."))
|
|
||||||
.or(nameStartsWith("com.newrelic."))
|
|
||||||
.or(nameStartsWith("com.dynatrace."))
|
|
||||||
.or(nameStartsWith("com.jloadtrace."))
|
|
||||||
.or(nameStartsWith("com.appdynamics."))
|
|
||||||
.or(nameStartsWith("com.singularity."))
|
|
||||||
.or(nameStartsWith("com.jinspired."))
|
|
||||||
.or(nameStartsWith("org.jinspired."))
|
|
||||||
.or(nameStartsWith("org.apache.log4j.").and(not(named("org.apache.log4j.MDC"))))
|
|
||||||
.or(nameStartsWith("org.slf4j.").and(not(named("org.slf4j.MDC"))))
|
|
||||||
.or(nameContains("$JaxbAccessor"))
|
|
||||||
.or(nameContains("CGLIB$$"))
|
|
||||||
.or(nameContains("javassist"))
|
|
||||||
.or(nameContains(".asm."))
|
|
||||||
.or(nameContains("$__sisu"))
|
|
||||||
.or(nameMatches("com\\.mchange\\.v2\\.c3p0\\..*Proxy"))
|
|
||||||
.or(isAnnotatedWith(named("javax.decorator.Decorator")))
|
|
||||||
.or(matchesConfiguredExcludes());
|
.or(matchesConfiguredExcludes());
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
agentBuilder =
|
||||||
|
agentBuilder
|
||||||
|
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
|
||||||
|
.with(new RedefinitionLoggingListener())
|
||||||
|
.with(new TransformLoggingListener());
|
||||||
|
}
|
||||||
|
|
||||||
for (final AgentBuilder.Listener listener : listeners) {
|
for (final AgentBuilder.Listener listener : listeners) {
|
||||||
agentBuilder = agentBuilder.with(listener);
|
agentBuilder = agentBuilder.with(listener);
|
||||||
}
|
}
|
||||||
|
@ -159,6 +99,23 @@ public class AgentInstaller {
|
||||||
return agentBuilder.installOn(inst);
|
return agentBuilder.installOn(inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void addByteBuddyRawSetting() {
|
||||||
|
final String savedPropertyValue = System.getProperty(TypeDefinition.RAW_TYPES_PROPERTY);
|
||||||
|
try {
|
||||||
|
System.setProperty(TypeDefinition.RAW_TYPES_PROPERTY, "true");
|
||||||
|
final boolean rawTypes = TypeDescription.AbstractBase.RAW_TYPES;
|
||||||
|
if (!rawTypes) {
|
||||||
|
log.debug("Too late to enable {}", TypeDefinition.RAW_TYPES_PROPERTY);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (savedPropertyValue == null) {
|
||||||
|
System.clearProperty(TypeDefinition.RAW_TYPES_PROPERTY);
|
||||||
|
} else {
|
||||||
|
System.setProperty(TypeDefinition.RAW_TYPES_PROPERTY, savedPropertyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static ElementMatcher.Junction<Object> matchesConfiguredExcludes() {
|
private static ElementMatcher.Junction<Object> matchesConfiguredExcludes() {
|
||||||
final List<String> excludedClasses = Config.get().getExcludedClasses();
|
final List<String> excludedClasses = Config.get().getExcludedClasses();
|
||||||
ElementMatcher.Junction matcher = none();
|
ElementMatcher.Junction matcher = none();
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling;
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.DDCachingPoolStrategy;
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.DDLocationStrategy;
|
||||||
import datadog.trace.bootstrap.WeakMap;
|
import datadog.trace.bootstrap.WeakMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,382 +0,0 @@
|
||||||
package datadog.trace.agent.tooling;
|
|
||||||
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.hasSignature;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
|
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
|
||||||
import net.bytebuddy.description.type.TypeDefinition;
|
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
|
||||||
import net.bytebuddy.description.type.TypeList;
|
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
|
||||||
import net.bytebuddy.matcher.ElementMatchers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class provides some custom ByteBuddy element matchers to use when applying instrumentation
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public class ByteBuddyElementMatchers {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches any type description that declares a super type that matches the provided matcher.
|
|
||||||
* Exceptions during matching process are logged and ignored.
|
|
||||||
*
|
|
||||||
* @param matcher The type to be checked for being a super type of the matched type.
|
|
||||||
* @param <T> The type of the matched object.
|
|
||||||
* @return A matcher that matches any type description that declares a super type that matches the
|
|
||||||
* provided matcher.
|
|
||||||
* @see ElementMatchers#hasSuperType(net.bytebuddy.matcher.ElementMatcher)
|
|
||||||
*/
|
|
||||||
public static <T extends TypeDescription> ElementMatcher.Junction<T> safeHasSuperType(
|
|
||||||
final ElementMatcher<? super TypeDescription> matcher) {
|
|
||||||
return safeHasGenericSuperType(new SafeErasureMatcher(matcher));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches any type description that declares a super type that matches the provided matcher.
|
|
||||||
* Exceptions during matching process are logged and ignored.
|
|
||||||
*
|
|
||||||
* @param matcher The type to be checked for being a super type of the matched type.
|
|
||||||
* @param <T> The type of the matched object.
|
|
||||||
* @return A matcher that matches any type description that declares a super type that matches the
|
|
||||||
* provided matcher.
|
|
||||||
* @see ElementMatchers#hasGenericSuperType(net.bytebuddy.matcher.ElementMatcher)
|
|
||||||
*/
|
|
||||||
public static <T extends TypeDescription> ElementMatcher.Junction<T> safeHasGenericSuperType(
|
|
||||||
final ElementMatcher<? super TypeDescription.Generic> matcher) {
|
|
||||||
return new SafeHasSuperTypeMatcher<>(matcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wraps another matcher to assure that an element is not matched in case that the matching causes
|
|
||||||
* an {@link Exception}. Logs exception if it happens.
|
|
||||||
*
|
|
||||||
* @param matcher The element matcher that potentially throws an exception.
|
|
||||||
* @param <T> The type of the matched object.
|
|
||||||
* @return A matcher that returns {@code false} in case that the given matcher throws an
|
|
||||||
* exception.
|
|
||||||
*/
|
|
||||||
public static <T> ElementMatcher.Junction<T> failSafe(
|
|
||||||
final ElementMatcher<? super T> matcher, final String description) {
|
|
||||||
return new SafeMatcher<>(matcher, false, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static TypeDescription safeAsErasure(final TypeDefinition typeDefinition) {
|
|
||||||
try {
|
|
||||||
return typeDefinition.asErasure();
|
|
||||||
} catch (final Exception e) {
|
|
||||||
log.debug(
|
|
||||||
"{} trying to get erasure for target {}: {}",
|
|
||||||
e.getClass().getSimpleName(),
|
|
||||||
safeTypeDefinitionName(typeDefinition),
|
|
||||||
e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An element matcher that matches a super type. This is different from {@link
|
|
||||||
* net.bytebuddy.matcher.HasSuperTypeMatcher} in the following way:
|
|
||||||
*
|
|
||||||
* <ul>
|
|
||||||
* <li>Exceptions are logged
|
|
||||||
* <li>When exception happens the rest of the inheritance subtree is discarded (since ByteBuddy
|
|
||||||
* cannot load/parse type information for it) but search in other subtrees continues
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <p>This is useful because this allows us to see when matcher's check is not complete (i.e. part
|
|
||||||
* of it fails), at the same time it makes best effort instead of failing quickly (like {@code
|
|
||||||
* failSafe(hasSuperType(...))} does) which means the code is more resilient to classpath
|
|
||||||
* inconsistencies
|
|
||||||
*
|
|
||||||
* @param <T> The type of the matched entity.
|
|
||||||
* @see net.bytebuddy.matcher.HasSuperTypeMatcher
|
|
||||||
*/
|
|
||||||
@HashCodeAndEqualsPlugin.Enhance
|
|
||||||
public static class SafeHasSuperTypeMatcher<T extends TypeDescription>
|
|
||||||
extends ElementMatcher.Junction.AbstractBase<T> {
|
|
||||||
|
|
||||||
/** The matcher to apply to any super type of the matched type. */
|
|
||||||
private final ElementMatcher<? super TypeDescription.Generic> matcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new matcher for a super type.
|
|
||||||
*
|
|
||||||
* @param matcher The matcher to apply to any super type of the matched type.
|
|
||||||
*/
|
|
||||||
public SafeHasSuperTypeMatcher(final ElementMatcher<? super TypeDescription.Generic> matcher) {
|
|
||||||
this.matcher = matcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(final T target) {
|
|
||||||
final Set<TypeDescription> checkedInterfaces = new HashSet<>();
|
|
||||||
// We do not use foreach loop and iterator interface here because we need to catch exceptions
|
|
||||||
// in {@code getSuperClass} calls
|
|
||||||
TypeDefinition typeDefinition = target;
|
|
||||||
while (typeDefinition != null) {
|
|
||||||
if (matcher.matches(typeDefinition.asGenericType())
|
|
||||||
|| hasInterface(typeDefinition, checkedInterfaces)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
typeDefinition = safeGetSuperClass(typeDefinition);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeDefinition safeGetSuperClass(final TypeDefinition typeDefinition) {
|
|
||||||
try {
|
|
||||||
return typeDefinition.getSuperClass();
|
|
||||||
} catch (final Exception e) {
|
|
||||||
log.debug(
|
|
||||||
"{} trying to get super class for target {}: {}",
|
|
||||||
e.getClass().getSimpleName(),
|
|
||||||
safeTypeDefinitionName(typeDefinition),
|
|
||||||
e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Matches a type's interfaces against the provided matcher.
|
|
||||||
*
|
|
||||||
* @param typeDefinition The type for which to check all implemented interfaces.
|
|
||||||
* @param checkedInterfaces The interfaces that have already been checked.
|
|
||||||
* @return {@code true} if any interface matches the supplied matcher.
|
|
||||||
*/
|
|
||||||
private boolean hasInterface(
|
|
||||||
final TypeDefinition typeDefinition, final Set<TypeDescription> checkedInterfaces) {
|
|
||||||
for (final TypeDefinition interfaceType : safeGetInterfaces(typeDefinition)) {
|
|
||||||
final TypeDescription erasure = safeAsErasure(interfaceType);
|
|
||||||
if (erasure != null) {
|
|
||||||
if (checkedInterfaces.add(interfaceType.asErasure())
|
|
||||||
&& (matcher.matches(interfaceType.asGenericType())
|
|
||||||
|| hasInterface(interfaceType, checkedInterfaces))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TypeDefinition#getInterfaces() produces an interator which may throw an exception during
|
|
||||||
* iteration if an interface is absent from the classpath.
|
|
||||||
*
|
|
||||||
* <p>This method exists to allow getting interfaces even if the lookup on one fails.
|
|
||||||
*/
|
|
||||||
private List<TypeDefinition> safeGetInterfaces(final TypeDefinition typeDefinition) {
|
|
||||||
final List<TypeDefinition> interfaceTypes = new ArrayList<>();
|
|
||||||
try {
|
|
||||||
final Iterator<TypeDescription.Generic> interfaceIter =
|
|
||||||
typeDefinition.getInterfaces().iterator();
|
|
||||||
while (interfaceIter.hasNext()) {
|
|
||||||
interfaceTypes.add(interfaceIter.next());
|
|
||||||
}
|
|
||||||
} catch (final Exception e) {
|
|
||||||
log.debug(
|
|
||||||
"{} trying to get interfaces for target {}: {}",
|
|
||||||
e.getClass().getSimpleName(),
|
|
||||||
safeTypeDefinitionName(typeDefinition),
|
|
||||||
e.getMessage());
|
|
||||||
}
|
|
||||||
return interfaceTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "safeHasSuperType(" + matcher + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An element matcher that matches its argument's {@link TypeDescription.Generic} raw type against
|
|
||||||
* the given matcher for a {@link TypeDescription}. As a wildcard does not define an erasure, a
|
|
||||||
* runtime exception is thrown when this matcher is applied to a wildcard.
|
|
||||||
*
|
|
||||||
* <p>Catches and logs exception if it was thrown when getting erasure, returning false.
|
|
||||||
*
|
|
||||||
* @param <T> The type of the matched entity.
|
|
||||||
* @see net.bytebuddy.matcher.ErasureMatcher
|
|
||||||
*/
|
|
||||||
@HashCodeAndEqualsPlugin.Enhance
|
|
||||||
public static class SafeErasureMatcher<T extends TypeDefinition>
|
|
||||||
extends ElementMatcher.Junction.AbstractBase<T> {
|
|
||||||
|
|
||||||
/** The matcher to apply to the raw type of the matched element. */
|
|
||||||
private final ElementMatcher<? super TypeDescription> matcher;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new erasure matcher.
|
|
||||||
*
|
|
||||||
* @param matcher The matcher to apply to the raw type.
|
|
||||||
*/
|
|
||||||
public SafeErasureMatcher(final ElementMatcher<? super TypeDescription> matcher) {
|
|
||||||
this.matcher = matcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(final T target) {
|
|
||||||
final TypeDescription erasure = safeAsErasure(target);
|
|
||||||
if (erasure == null) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
// We would like matcher exceptions to propagate
|
|
||||||
return matcher.matches(erasure);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "safeErasure(" + matcher + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A fail-safe matcher catches exceptions that are thrown by a delegate matcher and returns an
|
|
||||||
* alternative value.
|
|
||||||
*
|
|
||||||
* <p>Logs exception if it was thrown.
|
|
||||||
*
|
|
||||||
* @param <T> The type of the matched entity.
|
|
||||||
* @see net.bytebuddy.matcher.FailSafeMatcher
|
|
||||||
*/
|
|
||||||
@HashCodeAndEqualsPlugin.Enhance
|
|
||||||
public static class SafeMatcher<T> extends ElementMatcher.Junction.AbstractBase<T> {
|
|
||||||
|
|
||||||
/** The delegate matcher that might throw an exception. */
|
|
||||||
private final ElementMatcher<? super T> matcher;
|
|
||||||
|
|
||||||
/** The fallback value in case of an exception. */
|
|
||||||
private final boolean fallback;
|
|
||||||
|
|
||||||
/** The text description to log if exception happens. */
|
|
||||||
private final String description;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new fail-safe element matcher.
|
|
||||||
*
|
|
||||||
* @param matcher The delegate matcher that might throw an exception.
|
|
||||||
* @param fallback The fallback value in case of an exception.
|
|
||||||
* @param description Descriptive string to log along with exception.
|
|
||||||
*/
|
|
||||||
public SafeMatcher(
|
|
||||||
final ElementMatcher<? super T> matcher, final boolean fallback, final String description) {
|
|
||||||
this.matcher = matcher;
|
|
||||||
this.fallback = fallback;
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(final T target) {
|
|
||||||
try {
|
|
||||||
return matcher.matches(target);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
log.debug(description, e);
|
|
||||||
return fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "safeMatcher(try(" + matcher + ") or " + fallback + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String safeTypeDefinitionName(final TypeDefinition td) {
|
|
||||||
try {
|
|
||||||
return td.getTypeName();
|
|
||||||
} catch (final IllegalStateException ex) {
|
|
||||||
final String message = ex.getMessage();
|
|
||||||
if (message.startsWith("Cannot resolve type description for ")) {
|
|
||||||
return message.replace("Cannot resolve type description for ", "");
|
|
||||||
} else {
|
|
||||||
return "?";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add javadoc
|
|
||||||
public static <T extends MethodDescription> ElementMatcher.Junction<T> hasSuperMethod(
|
|
||||||
final ElementMatcher<? super MethodDescription> matcher) {
|
|
||||||
return new HasSuperMethodMatcher<>(matcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add javadoc
|
|
||||||
@HashCodeAndEqualsPlugin.Enhance
|
|
||||||
public static class HasSuperMethodMatcher<T extends MethodDescription>
|
|
||||||
extends ElementMatcher.Junction.AbstractBase<T> {
|
|
||||||
|
|
||||||
private final ElementMatcher<? super MethodDescription> matcher;
|
|
||||||
|
|
||||||
public HasSuperMethodMatcher(final ElementMatcher<? super MethodDescription> matcher) {
|
|
||||||
this.matcher = matcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(final MethodDescription target) {
|
|
||||||
if (target.isConstructor()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final Junction<MethodDescription> signatureMatcher = hasSignature(target.asSignatureToken());
|
|
||||||
TypeDefinition declaringType = target.getDeclaringType();
|
|
||||||
final Set<TypeDefinition> checkedInterfaces = new HashSet<>();
|
|
||||||
|
|
||||||
while (declaringType != null) {
|
|
||||||
for (final MethodDescription methodDescription : declaringType.getDeclaredMethods()) {
|
|
||||||
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matchesInterface(declaringType.getInterfaces(), signatureMatcher, checkedInterfaces)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
declaringType = safeGetSuperClass(declaringType);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean matchesInterface(
|
|
||||||
final TypeList.Generic interfaces,
|
|
||||||
final Junction<MethodDescription> signatureMatcher,
|
|
||||||
final Set<TypeDefinition> checkedInterfaces) {
|
|
||||||
for (final TypeDefinition type : interfaces) {
|
|
||||||
if (!checkedInterfaces.contains(type)) {
|
|
||||||
checkedInterfaces.add(type);
|
|
||||||
for (final MethodDescription methodDescription : type.getDeclaredMethods()) {
|
|
||||||
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matchesInterface(type.getInterfaces(), signatureMatcher, checkedInterfaces)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeDefinition safeGetSuperClass(final TypeDefinition typeDefinition) {
|
|
||||||
try {
|
|
||||||
return typeDefinition.getSuperClass();
|
|
||||||
} catch (final Exception e) {
|
|
||||||
log.debug(
|
|
||||||
"{} trying to get super class for target {}: {}",
|
|
||||||
e.getClass().getSimpleName(),
|
|
||||||
safeTypeDefinitionName(typeDefinition),
|
|
||||||
e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "hasSuperMethodMatcher(" + matcher + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +1,17 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling;
|
||||||
|
|
||||||
import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap;
|
import com.google.common.cache.Cache;
|
||||||
|
import com.google.common.cache.CacheBuilder;
|
||||||
import datadog.trace.bootstrap.DatadogClassLoader;
|
|
||||||
import datadog.trace.bootstrap.PatchLogger;
|
import datadog.trace.bootstrap.PatchLogger;
|
||||||
import datadog.trace.bootstrap.WeakMap;
|
|
||||||
import io.opentracing.util.GlobalTracer;
|
import io.opentracing.util.GlobalTracer;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ClassLoaderMatcher {
|
public final class ClassLoaderMatcher {
|
||||||
public static final ClassLoader BOOTSTRAP_CLASSLOADER = null;
|
public static final ClassLoader BOOTSTRAP_CLASSLOADER = null;
|
||||||
|
public static final int CACHE_CONCURRENCY =
|
||||||
|
Math.max(8, Runtime.getRuntime().availableProcessors());
|
||||||
|
|
||||||
/** A private constructor that must not be invoked. */
|
/** A private constructor that must not be invoked. */
|
||||||
private ClassLoaderMatcher() {
|
private ClassLoaderMatcher() {
|
||||||
|
@ -25,58 +22,49 @@ public class ClassLoaderMatcher {
|
||||||
return SkipClassLoaderMatcher.INSTANCE;
|
return SkipClassLoaderMatcher.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ElementMatcher.Junction.AbstractBase<ClassLoader> classLoaderHasClasses(
|
public static ElementMatcher.Junction.AbstractBase<ClassLoader> classLoaderHasNoResources(
|
||||||
final String... names) {
|
final String... resources) {
|
||||||
return new ClassLoaderHasClassMatcher(names);
|
return new ClassLoaderHasNoResourceMatcher(resources);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SkipClassLoaderMatcher
|
private static final class SkipClassLoaderMatcher
|
||||||
extends ElementMatcher.Junction.AbstractBase<ClassLoader>
|
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
|
||||||
implements WeakMap.ValueSupplier<ClassLoader, Boolean> {
|
|
||||||
public static final SkipClassLoaderMatcher INSTANCE = new SkipClassLoaderMatcher();
|
public static final SkipClassLoaderMatcher INSTANCE = new SkipClassLoaderMatcher();
|
||||||
/* Cache of classloader-instance -> (true|false). True = skip instrumentation. False = safe to instrument. */
|
/* Cache of classloader-instance -> (true|false). True = skip instrumentation. False = safe to instrument. */
|
||||||
private static final WeakMap<ClassLoader, Boolean> SKIP_CACHE = newWeakMap();
|
private static final Cache<ClassLoader, Boolean> skipCache =
|
||||||
private static final Set<String> CLASSLOADER_CLASSES_TO_SKIP;
|
CacheBuilder.newBuilder().weakKeys().concurrencyLevel(CACHE_CONCURRENCY).build();
|
||||||
|
private static final String DATADOG_CLASSLOADER_NAME =
|
||||||
static {
|
"datadog.trace.bootstrap.DatadogClassLoader";
|
||||||
final Set<String> classesToSkip = new HashSet<>();
|
|
||||||
classesToSkip.add("org.codehaus.groovy.runtime.callsite.CallSiteClassLoader");
|
|
||||||
classesToSkip.add("sun.reflect.DelegatingClassLoader");
|
|
||||||
classesToSkip.add("jdk.internal.reflect.DelegatingClassLoader");
|
|
||||||
classesToSkip.add("clojure.lang.DynamicClassLoader");
|
|
||||||
classesToSkip.add("org.apache.cxf.common.util.ASMHelper$TypeHelperClassLoader");
|
|
||||||
classesToSkip.add(DatadogClassLoader.class.getName());
|
|
||||||
CLASSLOADER_CLASSES_TO_SKIP = Collections.unmodifiableSet(classesToSkip);
|
|
||||||
}
|
|
||||||
|
|
||||||
private SkipClassLoaderMatcher() {}
|
private SkipClassLoaderMatcher() {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(final ClassLoader target) {
|
public boolean matches(final ClassLoader cl) {
|
||||||
if (target == BOOTSTRAP_CLASSLOADER) {
|
if (cl == BOOTSTRAP_CLASSLOADER) {
|
||||||
// Don't skip bootstrap loader
|
// Don't skip bootstrap loader
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return shouldSkipClass(target) || shouldSkipInstance(target);
|
Boolean v = skipCache.getIfPresent(cl);
|
||||||
}
|
if (v != null) {
|
||||||
|
return v;
|
||||||
private boolean shouldSkipClass(final ClassLoader loader) {
|
|
||||||
return CLASSLOADER_CLASSES_TO_SKIP.contains(loader.getClass().getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean shouldSkipInstance(final ClassLoader loader) {
|
|
||||||
return SKIP_CACHE.computeIfAbsent(loader, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Boolean get(final ClassLoader loader) {
|
|
||||||
final boolean skip = !delegatesToBootstrap(loader);
|
|
||||||
if (skip) {
|
|
||||||
log.debug(
|
|
||||||
"skipping classloader instance {} of type {}", loader, loader.getClass().getName());
|
|
||||||
}
|
}
|
||||||
|
v = shouldSkipClass(cl) || !delegatesToBootstrap(cl);
|
||||||
|
skipCache.put(cl, v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
return skip;
|
private static boolean shouldSkipClass(final ClassLoader loader) {
|
||||||
|
switch (loader.getClass().getName()) {
|
||||||
|
case "org.codehaus.groovy.runtime.callsite.CallSiteClassLoader":
|
||||||
|
case "sun.reflect.DelegatingClassLoader":
|
||||||
|
case "jdk.internal.reflect.DelegatingClassLoader":
|
||||||
|
case "clojure.lang.DynamicClassLoader":
|
||||||
|
case "org.apache.cxf.common.util.ASMHelper$TypeHelperClassLoader":
|
||||||
|
case "sun.misc.Launcher$ExtClassLoader":
|
||||||
|
case DATADOG_CLASSLOADER_NAME:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -85,7 +73,7 @@ public class ClassLoaderMatcher {
|
||||||
* class loading is issued from this check and {@code false} for 'real' class loads. We should
|
* class loading is issued from this check and {@code false} for 'real' class loads. We should
|
||||||
* come up with some sort of hack to avoid this problem.
|
* come up with some sort of hack to avoid this problem.
|
||||||
*/
|
*/
|
||||||
private boolean delegatesToBootstrap(final ClassLoader loader) {
|
private static boolean delegatesToBootstrap(final ClassLoader loader) {
|
||||||
boolean delegates = true;
|
boolean delegates = true;
|
||||||
if (!loadsExpectedClass(loader, GlobalTracer.class)) {
|
if (!loadsExpectedClass(loader, GlobalTracer.class)) {
|
||||||
log.debug("loader {} failed to delegate bootstrap opentracing class", loader);
|
log.debug("loader {} failed to delegate bootstrap opentracing class", loader);
|
||||||
|
@ -98,7 +86,8 @@ public class ClassLoaderMatcher {
|
||||||
return delegates;
|
return delegates;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadsExpectedClass(final ClassLoader loader, final Class<?> expectedClass) {
|
private static boolean loadsExpectedClass(
|
||||||
|
final ClassLoader loader, final Class<?> expectedClass) {
|
||||||
try {
|
try {
|
||||||
return loader.loadClass(expectedClass.getName()) == expectedClass;
|
return loader.loadClass(expectedClass.getName()) == expectedClass;
|
||||||
} catch (final ClassNotFoundException e) {
|
} catch (final ClassNotFoundException e) {
|
||||||
|
@ -107,36 +96,38 @@ public class ClassLoaderMatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ClassLoaderHasClassMatcher
|
private static class ClassLoaderHasNoResourceMatcher
|
||||||
extends ElementMatcher.Junction.AbstractBase<ClassLoader>
|
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
|
||||||
implements WeakMap.ValueSupplier<ClassLoader, Boolean> {
|
private final Cache<ClassLoader, Boolean> cache =
|
||||||
|
CacheBuilder.newBuilder().weakKeys().concurrencyLevel(CACHE_CONCURRENCY).build();
|
||||||
|
|
||||||
private final WeakMap<ClassLoader, Boolean> cache = newWeakMap();
|
private final String[] resources;
|
||||||
|
|
||||||
private final String[] names;
|
private ClassLoaderHasNoResourceMatcher(final String... resources) {
|
||||||
|
this.resources = resources;
|
||||||
private ClassLoaderHasClassMatcher(final String... names) {
|
|
||||||
this.names = names;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private boolean hasNoResources(ClassLoader cl) {
|
||||||
public boolean matches(final ClassLoader target) {
|
for (final String resource : resources) {
|
||||||
if (target != null) {
|
if (cl.getResource(resource) == null) {
|
||||||
return cache.computeIfAbsent(target, this);
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean get(final ClassLoader target) {
|
public boolean matches(final ClassLoader cl) {
|
||||||
for (final String name : names) {
|
if (cl == null) {
|
||||||
if (target.getResource(Utils.getResourceName(name)) == null) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Boolean v = cache.getIfPresent(cl);
|
||||||
return true;
|
if (v != null) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
v = hasNoResources(cl);
|
||||||
|
cache.put(cl, v);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.failSafe;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.failSafe;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.any;
|
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.DDTransformers;
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.ExceptionHandlers;
|
||||||
import datadog.trace.agent.tooling.context.FieldBackedProvider;
|
import datadog.trace.agent.tooling.context.FieldBackedProvider;
|
||||||
import datadog.trace.agent.tooling.context.InstrumentationContextProvider;
|
import datadog.trace.agent.tooling.context.InstrumentationContextProvider;
|
||||||
|
import datadog.trace.agent.tooling.context.NoopContextProvider;
|
||||||
import datadog.trace.agent.tooling.muzzle.Reference;
|
import datadog.trace.agent.tooling.muzzle.Reference;
|
||||||
import datadog.trace.agent.tooling.muzzle.ReferenceMatcher;
|
import datadog.trace.agent.tooling.muzzle.ReferenceMatcher;
|
||||||
import datadog.trace.api.Config;
|
import datadog.trace.api.Config;
|
||||||
|
@ -17,9 +23,11 @@ import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationSource;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher.Junction;
|
||||||
import net.bytebuddy.utility.JavaModule;
|
import net.bytebuddy.utility.JavaModule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,6 +47,12 @@ public interface Instrumenter {
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
abstract class Default implements Instrumenter {
|
abstract class Default implements Instrumenter {
|
||||||
|
|
||||||
|
// Added here instead of AgentInstaller's ignores because it's relatively
|
||||||
|
// expensive. https://github.com/DataDog/dd-trace-java/pull/1045
|
||||||
|
public static final Junction<AnnotationSource> NOT_DECORATOR_MATCHER =
|
||||||
|
not(isAnnotatedWith(named("javax.decorator.Decorator")));
|
||||||
|
|
||||||
private final SortedSet<String> instrumentationNames;
|
private final SortedSet<String> instrumentationNames;
|
||||||
private final String instrumentationPrimaryName;
|
private final String instrumentationPrimaryName;
|
||||||
private final InstrumentationContextProvider contextProvider;
|
private final InstrumentationContextProvider contextProvider;
|
||||||
|
@ -53,7 +67,11 @@ public interface Instrumenter {
|
||||||
instrumentationPrimaryName = instrumentationName;
|
instrumentationPrimaryName = instrumentationName;
|
||||||
|
|
||||||
enabled = Config.get().isIntegrationEnabled(instrumentationNames, defaultEnabled());
|
enabled = Config.get().isIntegrationEnabled(instrumentationNames, defaultEnabled());
|
||||||
contextProvider = new FieldBackedProvider(this);
|
if (contextStore().size() > 0) {
|
||||||
|
contextProvider = new FieldBackedProvider(this);
|
||||||
|
} else {
|
||||||
|
contextProvider = NoopContextProvider.INSTANCE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -73,6 +91,7 @@ public interface Instrumenter {
|
||||||
classLoaderMatcher(),
|
classLoaderMatcher(),
|
||||||
"Instrumentation class loader matcher unexpected exception: "
|
"Instrumentation class loader matcher unexpected exception: "
|
||||||
+ getClass().getName()))
|
+ getClass().getName()))
|
||||||
|
.and(NOT_DECORATOR_MATCHER)
|
||||||
.and(new MuzzleMatcher())
|
.and(new MuzzleMatcher())
|
||||||
.and(new PostMatchHook())
|
.and(new PostMatchHook())
|
||||||
.transform(DDTransformers.defaultTransformers());
|
.transform(DDTransformers.defaultTransformers());
|
||||||
|
@ -120,10 +139,11 @@ public interface Instrumenter {
|
||||||
*/
|
*/
|
||||||
final ReferenceMatcher muzzle = getInstrumentationMuzzle();
|
final ReferenceMatcher muzzle = getInstrumentationMuzzle();
|
||||||
if (null != muzzle) {
|
if (null != muzzle) {
|
||||||
final List<Reference.Mismatch> mismatches =
|
final boolean isMatch = muzzle.matches(classLoader);
|
||||||
muzzle.getMismatchedReferenceSources(classLoader);
|
if (!isMatch) {
|
||||||
if (mismatches.size() > 0) {
|
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
|
final List<Reference.Mismatch> mismatches =
|
||||||
|
muzzle.getMismatchedReferenceSources(classLoader);
|
||||||
log.debug(
|
log.debug(
|
||||||
"Instrumentation muzzled: {} -- {} on {}",
|
"Instrumentation muzzled: {} -- {} on {}",
|
||||||
instrumentationNames,
|
instrumentationNames,
|
||||||
|
@ -140,7 +160,7 @@ public interface Instrumenter {
|
||||||
Instrumenter.Default.this.getClass().getName(),
|
Instrumenter.Default.this.getClass().getName(),
|
||||||
classLoader);
|
classLoader);
|
||||||
}
|
}
|
||||||
return mismatches.size() == 0;
|
return isMatch;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -204,11 +224,13 @@ public interface Instrumenter {
|
||||||
public abstract Map<? extends ElementMatcher<? super MethodDescription>, String> transformers();
|
public abstract Map<? extends ElementMatcher<? super MethodDescription>, String> transformers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A map of {class-name -> context-class-name}. Keys (and their subclasses) will be associated
|
* Context stores to define for this instrumentation.
|
||||||
* with a context of the value.
|
*
|
||||||
|
* <p>A map of {class-name -> context-class-name}. Keys (and their subclasses) will be
|
||||||
|
* associated with a context of the value.
|
||||||
*/
|
*/
|
||||||
public Map<String, String> contextStore() {
|
public Map<String, String> contextStore() {
|
||||||
return Collections.EMPTY_MAP;
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean defaultEnabled() {
|
protected boolean defaultEnabled() {
|
||||||
|
|
|
@ -39,7 +39,7 @@ class WeakMapSuppliers {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <K, V> WeakMap<K, V> get() {
|
public <K, V> WeakMap<K, V> get() {
|
||||||
final WeakConcurrentMap<K, V> map = new WeakConcurrentMap<>(false);
|
final WeakConcurrentMap<K, V> map = new WeakConcurrentMap<>(false, true);
|
||||||
cleaner.scheduleCleaning(map, MapCleaner.CLEANER, CLEAN_FREQUENCY_SECONDS, TimeUnit.SECONDS);
|
cleaner.scheduleCleaning(map, MapCleaner.CLEANER, CLEAN_FREQUENCY_SECONDS, TimeUnit.SECONDS);
|
||||||
return new Adapter<>(map);
|
return new Adapter<>(map);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling.bytebuddy;
|
||||||
|
|
||||||
import static net.bytebuddy.agent.builder.AgentBuilder.PoolStrategy;
|
import static net.bytebuddy.agent.builder.AgentBuilder.PoolStrategy;
|
||||||
|
|
||||||
|
@ -6,7 +6,11 @@ import com.google.common.cache.Cache;
|
||||||
import com.google.common.cache.CacheBuilder;
|
import com.google.common.cache.CacheBuilder;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.bytebuddy.description.annotation.AnnotationList;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.method.MethodList;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeList;
|
||||||
import net.bytebuddy.dynamic.ClassFileLocator;
|
import net.bytebuddy.dynamic.ClassFileLocator;
|
||||||
import net.bytebuddy.pool.TypePool;
|
import net.bytebuddy.pool.TypePool;
|
||||||
|
|
||||||
|
@ -42,7 +46,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
static final int LOADER_CAPACITY = 64;
|
static final int LOADER_CAPACITY = 64;
|
||||||
static final int TYPE_CAPACITY = 64;
|
static final int TYPE_CAPACITY = 64;
|
||||||
|
|
||||||
static final int BOOTSTRAP_HASH = 0;
|
static final int BOOTSTRAP_HASH = 7236344; // Just a random number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache of recent ClassLoader WeakReferences; used to...
|
* Cache of recent ClassLoader WeakReferences; used to...
|
||||||
|
@ -89,16 +93,16 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
loaderRefCache.put(classLoader, loaderRef);
|
loaderRefCache.put(classLoader, loaderRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
int loaderHash = classLoader.hashCode();
|
final int loaderHash = classLoader.hashCode();
|
||||||
return createCachingTypePool(loaderHash, loaderRef, classFileLocator);
|
return createCachingTypePool(loaderHash, loaderRef, classFileLocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final TypePool.CacheProvider createCacheProvider(
|
private TypePool.CacheProvider createCacheProvider(
|
||||||
final int loaderHash, final WeakReference<ClassLoader> loaderRef) {
|
final int loaderHash, final WeakReference<ClassLoader> loaderRef) {
|
||||||
return new SharedResolutionCacheAdapter(loaderHash, loaderRef, sharedResolutionCache);
|
return new SharedResolutionCacheAdapter(loaderHash, loaderRef, sharedResolutionCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final TypePool createCachingTypePool(
|
private TypePool createCachingTypePool(
|
||||||
final int loaderHash,
|
final int loaderHash,
|
||||||
final WeakReference<ClassLoader> loaderRef,
|
final WeakReference<ClassLoader> loaderRef,
|
||||||
final ClassFileLocator classFileLocator) {
|
final ClassFileLocator classFileLocator) {
|
||||||
|
@ -108,7 +112,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
TypePool.Default.ReaderMode.FAST);
|
TypePool.Default.ReaderMode.FAST);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final TypePool createCachingTypePool(
|
private TypePool createCachingTypePool(
|
||||||
final TypePool.CacheProvider cacheProvider, final ClassFileLocator classFileLocator) {
|
final TypePool.CacheProvider cacheProvider, final ClassFileLocator classFileLocator) {
|
||||||
return new TypePool.Default.WithLazyResolution(
|
return new TypePool.Default.WithLazyResolution(
|
||||||
cacheProvider, classFileLocator, TypePool.Default.ReaderMode.FAST);
|
cacheProvider, classFileLocator, TypePool.Default.ReaderMode.FAST);
|
||||||
|
@ -140,7 +144,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
this.loaderRef = loaderRef;
|
this.loaderRef = loaderRef;
|
||||||
this.className = className;
|
this.className = className;
|
||||||
|
|
||||||
hashCode = (int) (31 * this.loaderHash) ^ className.hashCode();
|
hashCode = 31 * this.loaderHash + className.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -150,18 +154,23 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(final Object obj) {
|
public boolean equals(final Object obj) {
|
||||||
if (!(obj instanceof TypeCacheKey)) return false;
|
if (!(obj instanceof TypeCacheKey)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
TypeCacheKey that = (TypeCacheKey) obj;
|
final TypeCacheKey that = (TypeCacheKey) obj;
|
||||||
|
|
||||||
if (loaderHash != that.loaderHash) return false;
|
if (loaderHash != that.loaderHash) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (className.equals(that.className)) {
|
||||||
|
// Fastpath loaderRef equivalence -- works because of WeakReference cache used
|
||||||
|
// Also covers the bootstrap null loaderRef case
|
||||||
|
if (loaderRef == that.loaderRef) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Fastpath loaderRef equivalence -- works because of WeakReference cache used
|
|
||||||
// Also covers the bootstrap null loaderRef case
|
|
||||||
if (loaderRef == that.loaderRef) {
|
|
||||||
// still need to check name
|
|
||||||
return className.equals(that.className);
|
|
||||||
} else if (className.equals(that.className)) {
|
|
||||||
// need to perform a deeper loader check -- requires calling Reference.get
|
// need to perform a deeper loader check -- requires calling Reference.get
|
||||||
// which can strengthen the Reference, so deliberately done last
|
// which can strengthen the Reference, so deliberately done last
|
||||||
|
|
||||||
|
@ -172,11 +181,15 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
// In this case, it is fine because that means the ClassLoader is no
|
// In this case, it is fine because that means the ClassLoader is no
|
||||||
// longer live, so the entries will never match anyway and will fall
|
// longer live, so the entries will never match anyway and will fall
|
||||||
// out of the cache.
|
// out of the cache.
|
||||||
ClassLoader thisLoader = loaderRef.get();
|
final ClassLoader thisLoader = loaderRef.get();
|
||||||
if (thisLoader == null) return false;
|
if (thisLoader == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
ClassLoader thatLoader = that.loaderRef.get();
|
final ClassLoader thatLoader = that.loaderRef.get();
|
||||||
if (thatLoader == null) return false;
|
if (thatLoader == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return (thisLoader == thatLoader);
|
return (thisLoader == thatLoader);
|
||||||
} else {
|
} else {
|
||||||
|
@ -188,7 +201,7 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
static final class SharedResolutionCacheAdapter implements TypePool.CacheProvider {
|
static final class SharedResolutionCacheAdapter implements TypePool.CacheProvider {
|
||||||
private static final String OBJECT_NAME = "java.lang.Object";
|
private static final String OBJECT_NAME = "java.lang.Object";
|
||||||
private static final TypePool.Resolution OBJECT_RESOLUTION =
|
private static final TypePool.Resolution OBJECT_RESOLUTION =
|
||||||
new TypePool.Resolution.Simple(TypeDescription.OBJECT);
|
new TypePool.Resolution.Simple(new CachingTypeDescription(TypeDescription.OBJECT));
|
||||||
|
|
||||||
private final int loaderHash;
|
private final int loaderHash;
|
||||||
private final WeakReference<ClassLoader> loaderRef;
|
private final WeakReference<ClassLoader> loaderRef;
|
||||||
|
@ -205,9 +218,11 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TypePool.Resolution find(final String className) {
|
public TypePool.Resolution find(final String className) {
|
||||||
TypePool.Resolution existingResolution =
|
final TypePool.Resolution existingResolution =
|
||||||
sharedResolutionCache.getIfPresent(new TypeCacheKey(loaderHash, loaderRef, className));
|
sharedResolutionCache.getIfPresent(new TypeCacheKey(loaderHash, loaderRef, className));
|
||||||
if (existingResolution != null) return existingResolution;
|
if (existingResolution != null) {
|
||||||
|
return existingResolution;
|
||||||
|
}
|
||||||
|
|
||||||
if (OBJECT_NAME.equals(className)) {
|
if (OBJECT_NAME.equals(className)) {
|
||||||
return OBJECT_RESOLUTION;
|
return OBJECT_RESOLUTION;
|
||||||
|
@ -217,12 +232,13 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TypePool.Resolution register(
|
public TypePool.Resolution register(final String className, TypePool.Resolution resolution) {
|
||||||
final String className, final TypePool.Resolution resolution) {
|
|
||||||
if (OBJECT_NAME.equals(className)) {
|
if (OBJECT_NAME.equals(className)) {
|
||||||
return resolution;
|
return resolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resolution = new CachingResolution(resolution);
|
||||||
|
|
||||||
sharedResolutionCache.put(new TypeCacheKey(loaderHash, loaderRef, className), resolution);
|
sharedResolutionCache.put(new TypeCacheKey(loaderHash, loaderRef, className), resolution);
|
||||||
return resolution;
|
return resolution;
|
||||||
}
|
}
|
||||||
|
@ -232,4 +248,90 @@ public class DDCachingPoolStrategy implements PoolStrategy {
|
||||||
// Allowing the high-level eviction policy make the clearing decisions
|
// Allowing the high-level eviction policy make the clearing decisions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class CachingResolution implements TypePool.Resolution {
|
||||||
|
private final TypePool.Resolution delegate;
|
||||||
|
private TypeDescription cachedResolution;
|
||||||
|
|
||||||
|
public CachingResolution(final TypePool.Resolution delegate) {
|
||||||
|
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isResolved() {
|
||||||
|
return delegate.isResolved();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeDescription resolve() {
|
||||||
|
// Intentionally not "thread safe". Duplicate work deemed an acceptable trade-off.
|
||||||
|
if (cachedResolution == null) {
|
||||||
|
cachedResolution = new CachingTypeDescription(delegate.resolve());
|
||||||
|
}
|
||||||
|
return cachedResolution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TypeDescription implementation that delegates and caches the results for the expensive calls
|
||||||
|
* commonly used by our instrumentation.
|
||||||
|
*/
|
||||||
|
private static class CachingTypeDescription
|
||||||
|
extends TypeDescription.AbstractBase.OfSimpleType.WithDelegation {
|
||||||
|
private final TypeDescription delegate;
|
||||||
|
|
||||||
|
// These fields are intentionally not "thread safe".
|
||||||
|
// Duplicate work deemed an acceptable trade-off.
|
||||||
|
private Generic superClass;
|
||||||
|
private TypeList.Generic interfaces;
|
||||||
|
private AnnotationList annotations;
|
||||||
|
private MethodList<MethodDescription.InDefinedShape> methods;
|
||||||
|
|
||||||
|
public CachingTypeDescription(final TypeDescription delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TypeDescription delegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Generic getSuperClass() {
|
||||||
|
if (superClass == null) {
|
||||||
|
superClass = delegate.getSuperClass();
|
||||||
|
}
|
||||||
|
return superClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeList.Generic getInterfaces() {
|
||||||
|
if (interfaces == null) {
|
||||||
|
interfaces = delegate.getInterfaces();
|
||||||
|
}
|
||||||
|
return interfaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationList getDeclaredAnnotations() {
|
||||||
|
if (annotations == null) {
|
||||||
|
annotations = delegate.getDeclaredAnnotations();
|
||||||
|
}
|
||||||
|
return annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodList<MethodDescription.InDefinedShape> getDeclaredMethods() {
|
||||||
|
if (methods == null) {
|
||||||
|
methods = delegate.getDeclaredMethods();
|
||||||
|
}
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return delegate.getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling.bytebuddy;
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.Utils;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling.bytebuddy;
|
||||||
|
|
||||||
import net.bytebuddy.agent.builder.AgentBuilder;
|
import net.bytebuddy.agent.builder.AgentBuilder;
|
||||||
import net.bytebuddy.asm.TypeConstantAdjustment;
|
import net.bytebuddy.asm.TypeConstantAdjustment;
|
||||||
|
@ -12,10 +12,10 @@ public class DDTransformers {
|
||||||
new AgentBuilder.Transformer() {
|
new AgentBuilder.Transformer() {
|
||||||
@Override
|
@Override
|
||||||
public DynamicType.Builder<?> transform(
|
public DynamicType.Builder<?> transform(
|
||||||
DynamicType.Builder<?> builder,
|
final DynamicType.Builder<?> builder,
|
||||||
TypeDescription typeDescription,
|
final TypeDescription typeDescription,
|
||||||
ClassLoader classLoader,
|
final ClassLoader classLoader,
|
||||||
JavaModule javaModule) {
|
final JavaModule javaModule) {
|
||||||
return builder.visit(TypeConstantAdjustment.INSTANCE);
|
return builder.visit(TypeConstantAdjustment.INSTANCE);
|
||||||
}
|
}
|
||||||
};
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling.bytebuddy;
|
||||||
|
|
||||||
import datadog.trace.bootstrap.ExceptionLogger;
|
import datadog.trace.bootstrap.ExceptionLogger;
|
||||||
import net.bytebuddy.ClassFileVersion;
|
import net.bytebuddy.ClassFileVersion;
|
|
@ -0,0 +1,162 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@HashCodeAndEqualsPlugin.Enhance
|
||||||
|
public class GlobalIgnoresMatcher<T extends TypeDescription>
|
||||||
|
extends ElementMatcher.Junction.AbstractBase<T> {
|
||||||
|
|
||||||
|
private static final Pattern COM_MCHANGE_PROXY =
|
||||||
|
Pattern.compile("com\\.mchange\\.v2\\.c3p0\\..*Proxy");
|
||||||
|
|
||||||
|
public static <T extends TypeDescription> ElementMatcher.Junction<T> globalIgnoresMatcher() {
|
||||||
|
return new GlobalIgnoresMatcher<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Be very careful about the types of matchers used in this section as they are called on every
|
||||||
|
* class load, so they must be fast. Generally speaking try to only use name matchers as they
|
||||||
|
* don't have to load additional info.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean matches(final T target) {
|
||||||
|
final String name = target.getActualName();
|
||||||
|
|
||||||
|
if (name.startsWith("datadog.opentracing.")
|
||||||
|
|| name.startsWith("datadog.slf4j.")
|
||||||
|
|| name.startsWith("net.bytebuddy.")
|
||||||
|
|| name.startsWith("jdk.")
|
||||||
|
|| name.startsWith("org.aspectj.")
|
||||||
|
|| name.startsWith("org.groovy.")
|
||||||
|
|| name.startsWith("org.codehaus.groovy.macro.")
|
||||||
|
|| name.startsWith("com.intellij.rt.debugger.")
|
||||||
|
|| name.startsWith("com.p6spy.")
|
||||||
|
|| name.startsWith("com.newrelic.")
|
||||||
|
|| name.startsWith("com.dynatrace.")
|
||||||
|
|| name.startsWith("com.jloadtrace.")
|
||||||
|
|| name.startsWith("com.appdynamics.")
|
||||||
|
|| name.startsWith("com.singularity.")
|
||||||
|
|| name.startsWith("com.jinspired.")
|
||||||
|
|| name.startsWith("org.jinspired.")
|
||||||
|
|| name.startsWith("org.springframework.cglib.")
|
||||||
|
|| name.startsWith("org.springframework.aop.")
|
||||||
|
|| name.startsWith("org.springframework.beans.factory.annotation.")
|
||||||
|
|| name.startsWith("org.springframework.beans.factory.config.")
|
||||||
|
|| name.startsWith("org.springframework.beans.factory.parsing.")
|
||||||
|
|| name.startsWith("org.springframework.beans.factory.xml.")
|
||||||
|
|| name.startsWith("org.springframework.beans.propertyeditors.")
|
||||||
|
|| name.startsWith("org.springframework.boot.autoconfigure.cache.")
|
||||||
|
|| name.startsWith("org.springframework.boot.autoconfigure.condition.")
|
||||||
|
|| name.startsWith("org.springframework.boot.autoconfigure.http.")
|
||||||
|
|| name.startsWith("org.springframework.boot.autoconfigure.jackson.")
|
||||||
|
|| name.startsWith("org.springframework.boot.autoconfigure.web.")
|
||||||
|
|| name.startsWith("org.springframework.boot.context.")
|
||||||
|
|| name.startsWith("org.springframework.boot.convert.")
|
||||||
|
|| name.startsWith("org.springframework.boot.diagnostics.")
|
||||||
|
|| name.startsWith("org.springframework.boot.web.server.")
|
||||||
|
|| name.startsWith("org.springframework.boot.web.servlet.")
|
||||||
|
|| name.startsWith("org.springframework.context.annotation.")
|
||||||
|
|| name.startsWith("org.springframework.context.event.")
|
||||||
|
|| name.startsWith("org.springframework.context.expression.")
|
||||||
|
|| name.startsWith("org.springframework.core.annotation.")
|
||||||
|
|| name.startsWith("org.springframework.core.convert.")
|
||||||
|
|| name.startsWith("org.springframework.core.env.")
|
||||||
|
|| name.startsWith("org.springframework.core.io.")
|
||||||
|
|| name.startsWith("org.springframework.core.type.")
|
||||||
|
|| name.startsWith("org.springframework.expression.")
|
||||||
|
|| name.startsWith("org.springframework.format.")
|
||||||
|
|| name.startsWith("org.springframework.http.")
|
||||||
|
|| name.startsWith("org.springframework.ui.")
|
||||||
|
|| name.startsWith("org.springframework.validation.")
|
||||||
|
|| name.startsWith("org.springframework.web.context.")
|
||||||
|
|| name.startsWith("org.springframework.web.filter.")
|
||||||
|
|| name.startsWith("org.springframework.web.method.")
|
||||||
|
|| name.startsWith("org.springframework.web.multipart.")
|
||||||
|
|| name.startsWith("org.springframework.web.util.")) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("datadog.trace.")) {
|
||||||
|
// FIXME: We should remove this once
|
||||||
|
// https://github.com/raphw/byte-buddy/issues/558 is fixed
|
||||||
|
if (name.equals("datadog.trace.bootstrap.instrumentation.java.concurrent.RunnableWrapper")
|
||||||
|
|| name.equals(
|
||||||
|
"datadog.trace.bootstrap.instrumentation.java.concurrent.CallableWrapper")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("java.")) {
|
||||||
|
if (name.equals("java.net.URL") || name.equals("java.net.HttpURLConnection")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (name.startsWith("java.rmi.") || name.startsWith("java.util.concurrent.")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Concurrent instrumentation modifies the structure of
|
||||||
|
// Cleaner class incompatibly with java9+ modules.
|
||||||
|
// Working around until a long-term fix for modules can be
|
||||||
|
// put in place.
|
||||||
|
if (name.startsWith("java.util.logging.")
|
||||||
|
&& !name.equals("java.util.logging.LogManager$Cleaner")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("com.sun.")) {
|
||||||
|
if (name.startsWith("com.sun.messaging.") || name.startsWith("com.sun.jersey.api.client")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("sun.")) {
|
||||||
|
if (name.startsWith("sun.net.www.protocol.")
|
||||||
|
|| name.startsWith("sun.rmi.server")
|
||||||
|
|| name.startsWith("sun.rmi.transport")
|
||||||
|
|| name.equals("sun.net.www.http.HttpClient")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("org.apache.log4j.")) {
|
||||||
|
if (name.equals("org.apache.log4j.MDC")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.startsWith("org.slf4j.")) {
|
||||||
|
if (name.equals("org.slf4j.MDC")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.contains("$JaxbAccessor")
|
||||||
|
|| name.contains("CGLIB$$")
|
||||||
|
|| name.contains("javassist")
|
||||||
|
|| name.contains(".asm.")
|
||||||
|
|| name.contains("$__sisu")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (COM_MCHANGE_PROXY.matcher(name).matches()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher;
|
||||||
|
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provides some custom ByteBuddy element matchers to use when applying instrumentation
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class DDElementMatchers {
|
||||||
|
|
||||||
|
public static <T extends TypeDescription> ElementMatcher.Junction<T> extendsClass(
|
||||||
|
final ElementMatcher<? super TypeDescription> matcher) {
|
||||||
|
return not(isInterface()).and(new SafeExtendsClassMatcher<>(new SafeErasureMatcher<>(matcher)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends TypeDescription> ElementMatcher.Junction<T> implementsInterface(
|
||||||
|
final ElementMatcher<? super TypeDescription> matcher) {
|
||||||
|
return not(isInterface())
|
||||||
|
.and(new SafeHasSuperTypeMatcher<>(new SafeErasureMatcher<>(matcher), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends TypeDescription> ElementMatcher.Junction<T> hasInterface(
|
||||||
|
final ElementMatcher<? super TypeDescription> matcher) {
|
||||||
|
return new SafeHasSuperTypeMatcher<>(new SafeErasureMatcher<>(matcher), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends TypeDescription> ElementMatcher.Junction<T> safeHasSuperType(
|
||||||
|
final ElementMatcher<? super TypeDescription> matcher) {
|
||||||
|
return not(isInterface())
|
||||||
|
.and(new SafeHasSuperTypeMatcher<>(new SafeErasureMatcher<>(matcher), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add javadoc
|
||||||
|
public static <T extends MethodDescription> ElementMatcher.Junction<T> hasSuperMethod(
|
||||||
|
final ElementMatcher<? super MethodDescription> matcher) {
|
||||||
|
return new HasSuperMethodMatcher<>(matcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps another matcher to assure that an element is not matched in case that the matching causes
|
||||||
|
* an {@link Exception}. Logs exception if it happens.
|
||||||
|
*
|
||||||
|
* @param matcher The element matcher that potentially throws an exception.
|
||||||
|
* @param <T> The type of the matched object.
|
||||||
|
* @return A matcher that returns {@code false} in case that the given matcher throws an
|
||||||
|
* exception.
|
||||||
|
*/
|
||||||
|
public static <T> ElementMatcher.Junction<T> failSafe(
|
||||||
|
final ElementMatcher<? super T> matcher, final String description) {
|
||||||
|
return new LoggingFailSafeMatcher<>(matcher, false, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String safeTypeDefinitionName(final TypeDefinition td) {
|
||||||
|
try {
|
||||||
|
return td.getTypeName();
|
||||||
|
} catch (final IllegalStateException ex) {
|
||||||
|
final String message = ex.getMessage();
|
||||||
|
if (message.startsWith("Cannot resolve type description for ")) {
|
||||||
|
return message.replace("Cannot resolve type description for ", "");
|
||||||
|
} else {
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.SafeHasSuperTypeMatcher.safeGetSuperClass;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.hasSignature;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
|
import net.bytebuddy.description.type.TypeList;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
// TODO: add javadoc
|
||||||
|
@HashCodeAndEqualsPlugin.Enhance
|
||||||
|
class HasSuperMethodMatcher<T extends MethodDescription>
|
||||||
|
extends ElementMatcher.Junction.AbstractBase<T> {
|
||||||
|
|
||||||
|
private final ElementMatcher<? super MethodDescription> matcher;
|
||||||
|
|
||||||
|
public HasSuperMethodMatcher(final ElementMatcher<? super MethodDescription> matcher) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(final MethodDescription target) {
|
||||||
|
if (target.isConstructor()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final Junction<MethodDescription> signatureMatcher = hasSignature(target.asSignatureToken());
|
||||||
|
TypeDefinition declaringType = target.getDeclaringType();
|
||||||
|
final Set<TypeDefinition> checkedInterfaces = new HashSet<>();
|
||||||
|
|
||||||
|
while (declaringType != null) {
|
||||||
|
for (final MethodDescription methodDescription : declaringType.getDeclaredMethods()) {
|
||||||
|
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchesInterface(declaringType.getInterfaces(), signatureMatcher, checkedInterfaces)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
declaringType = safeGetSuperClass(declaringType);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchesInterface(
|
||||||
|
final TypeList.Generic interfaces,
|
||||||
|
final Junction<MethodDescription> signatureMatcher,
|
||||||
|
final Set<TypeDefinition> checkedInterfaces) {
|
||||||
|
for (final TypeDefinition type : interfaces) {
|
||||||
|
if (!checkedInterfaces.contains(type)) {
|
||||||
|
checkedInterfaces.add(type);
|
||||||
|
for (final MethodDescription methodDescription : type.getDeclaredMethods()) {
|
||||||
|
if (signatureMatcher.matches(methodDescription) && matcher.matches(methodDescription)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (matchesInterface(type.getInterfaces(), signatureMatcher, checkedInterfaces)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "hasSuperMethodMatcher(" + matcher + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fail-safe matcher catches exceptions that are thrown by a delegate matcher and returns an
|
||||||
|
* alternative value.
|
||||||
|
*
|
||||||
|
* <p>Logs exception if it was thrown.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the matched entity.
|
||||||
|
* @see net.bytebuddy.matcher.FailSafeMatcher
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@HashCodeAndEqualsPlugin.Enhance
|
||||||
|
class LoggingFailSafeMatcher<T> extends ElementMatcher.Junction.AbstractBase<T> {
|
||||||
|
|
||||||
|
/** The delegate matcher that might throw an exception. */
|
||||||
|
private final ElementMatcher<? super T> matcher;
|
||||||
|
|
||||||
|
/** The fallback value in case of an exception. */
|
||||||
|
private final boolean fallback;
|
||||||
|
|
||||||
|
/** The text description to log if exception happens. */
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new fail-safe element matcher.
|
||||||
|
*
|
||||||
|
* @param matcher The delegate matcher that might throw an exception.
|
||||||
|
* @param fallback The fallback value in case of an exception.
|
||||||
|
* @param description Descriptive string to log along with exception.
|
||||||
|
*/
|
||||||
|
public LoggingFailSafeMatcher(
|
||||||
|
final ElementMatcher<? super T> matcher, final boolean fallback, final String description) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
this.fallback = fallback;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(final T target) {
|
||||||
|
try {
|
||||||
|
return matcher.matches(target);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.debug(description, e);
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "safeMatcher(try(" + matcher + ") or " + fallback + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeTypeDefinitionName;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An element matcher that matches its argument's {@link TypeDescription.Generic} raw type against
|
||||||
|
* the given matcher for a {@link TypeDescription}. As a wildcard does not define an erasure, a
|
||||||
|
* runtime exception is thrown when this matcher is applied to a wildcard.
|
||||||
|
*
|
||||||
|
* <p>Catches and logs exception if it was thrown when getting erasure, returning false.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the matched entity.
|
||||||
|
* @see net.bytebuddy.matcher.ErasureMatcher
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@HashCodeAndEqualsPlugin.Enhance
|
||||||
|
class SafeErasureMatcher<T extends TypeDefinition> extends ElementMatcher.Junction.AbstractBase<T> {
|
||||||
|
|
||||||
|
/** The matcher to apply to the raw type of the matched element. */
|
||||||
|
private final ElementMatcher<? super TypeDescription> matcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new erasure matcher.
|
||||||
|
*
|
||||||
|
* @param matcher The matcher to apply to the raw type.
|
||||||
|
*/
|
||||||
|
public SafeErasureMatcher(final ElementMatcher<? super TypeDescription> matcher) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(final T target) {
|
||||||
|
final TypeDescription erasure = safeAsErasure(target);
|
||||||
|
if (erasure == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// We would like matcher exceptions to propagate
|
||||||
|
return matcher.matches(erasure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "safeErasure(" + matcher + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeDescription safeAsErasure(final TypeDefinition typeDefinition) {
|
||||||
|
try {
|
||||||
|
return typeDefinition.asErasure();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.debug(
|
||||||
|
"{} trying to get erasure for target {}: {}",
|
||||||
|
e.getClass().getSimpleName(),
|
||||||
|
safeTypeDefinitionName(typeDefinition),
|
||||||
|
e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.SafeHasSuperTypeMatcher.safeGetSuperClass;
|
||||||
|
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
class SafeExtendsClassMatcher<T extends TypeDescription>
|
||||||
|
extends ElementMatcher.Junction.AbstractBase<T> {
|
||||||
|
|
||||||
|
private final ElementMatcher<? super TypeDescription.Generic> matcher;
|
||||||
|
|
||||||
|
public SafeExtendsClassMatcher(final ElementMatcher<? super TypeDescription.Generic> matcher) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(final T target) {
|
||||||
|
// We do not use foreach loop and iterator interface here because we need to catch exceptions
|
||||||
|
// in {@code getSuperClass} calls
|
||||||
|
TypeDefinition typeDefinition = target;
|
||||||
|
while (typeDefinition != null) {
|
||||||
|
if (matcher.matches(typeDefinition.asGenericType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
typeDefinition = safeGetSuperClass(typeDefinition);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "safeExtendsClass(" + matcher + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeTypeDefinitionName;
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.SafeErasureMatcher.safeAsErasure;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
|
||||||
|
import net.bytebuddy.description.type.TypeDefinition;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An element matcher that matches a super type. This is different from {@link
|
||||||
|
* net.bytebuddy.matcher.HasSuperTypeMatcher} in the following way:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Exceptions are logged
|
||||||
|
* <li>When exception happens the rest of the inheritance subtree is discarded (since ByteBuddy
|
||||||
|
* cannot load/parse type information for it) but search in other subtrees continues
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>This is useful because this allows us to see when matcher's check is not complete (i.e. part
|
||||||
|
* of it fails), at the same time it makes best effort instead of failing quickly (like {@code
|
||||||
|
* failSafe(hasSuperType(...))} does) which means the code is more resilient to classpath
|
||||||
|
* inconsistencies
|
||||||
|
*
|
||||||
|
* @param <T> The type of the matched entity.
|
||||||
|
* @see net.bytebuddy.matcher.HasSuperTypeMatcher
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@HashCodeAndEqualsPlugin.Enhance
|
||||||
|
class SafeHasSuperTypeMatcher<T extends TypeDescription>
|
||||||
|
extends ElementMatcher.Junction.AbstractBase<T> {
|
||||||
|
|
||||||
|
/** The matcher to apply to any super type of the matched type. */
|
||||||
|
private final ElementMatcher<? super TypeDescription.Generic> matcher;
|
||||||
|
|
||||||
|
private final boolean interfacesOnly;
|
||||||
|
/**
|
||||||
|
* Creates a new matcher for a super type.
|
||||||
|
*
|
||||||
|
* @param matcher The matcher to apply to any super type of the matched type.
|
||||||
|
*/
|
||||||
|
public SafeHasSuperTypeMatcher(
|
||||||
|
final ElementMatcher<? super TypeDescription.Generic> matcher, final boolean interfacesOnly) {
|
||||||
|
this.matcher = matcher;
|
||||||
|
this.interfacesOnly = interfacesOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(final T target) {
|
||||||
|
final Set<TypeDescription> checkedInterfaces = new HashSet<>();
|
||||||
|
// We do not use foreach loop and iterator interface here because we need to catch exceptions
|
||||||
|
// in {@code getSuperClass} calls
|
||||||
|
TypeDefinition typeDefinition = target;
|
||||||
|
while (typeDefinition != null) {
|
||||||
|
if (((!interfacesOnly || typeDefinition.isInterface())
|
||||||
|
&& matcher.matches(typeDefinition.asGenericType()))
|
||||||
|
|| hasInterface(typeDefinition, checkedInterfaces)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
typeDefinition = safeGetSuperClass(typeDefinition);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Matches a type's interfaces against the provided matcher.
|
||||||
|
*
|
||||||
|
* @param typeDefinition The type for which to check all implemented interfaces.
|
||||||
|
* @param checkedInterfaces The interfaces that have already been checked.
|
||||||
|
* @return {@code true} if any interface matches the supplied matcher.
|
||||||
|
*/
|
||||||
|
private boolean hasInterface(
|
||||||
|
final TypeDefinition typeDefinition, final Set<TypeDescription> checkedInterfaces) {
|
||||||
|
for (final TypeDefinition interfaceType : safeGetInterfaces(typeDefinition)) {
|
||||||
|
final TypeDescription erasure = safeAsErasure(interfaceType);
|
||||||
|
if (erasure != null) {
|
||||||
|
if (checkedInterfaces.add(interfaceType.asErasure())
|
||||||
|
&& (matcher.matches(interfaceType.asGenericType())
|
||||||
|
|| hasInterface(interfaceType, checkedInterfaces))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TypeDefinition#getInterfaces() produces an interator which may throw an exception during
|
||||||
|
* iteration if an interface is absent from the classpath.
|
||||||
|
*
|
||||||
|
* <p>This method exists to allow getting interfaces even if the lookup on one fails.
|
||||||
|
*/
|
||||||
|
private List<TypeDefinition> safeGetInterfaces(final TypeDefinition typeDefinition) {
|
||||||
|
final List<TypeDefinition> interfaceTypes = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
final Iterator<TypeDescription.Generic> interfaceIter =
|
||||||
|
typeDefinition.getInterfaces().iterator();
|
||||||
|
while (interfaceIter.hasNext()) {
|
||||||
|
interfaceTypes.add(interfaceIter.next());
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.debug(
|
||||||
|
"{} trying to get interfaces for target {}: {}",
|
||||||
|
e.getClass().getSimpleName(),
|
||||||
|
safeTypeDefinitionName(typeDefinition),
|
||||||
|
e.getMessage());
|
||||||
|
}
|
||||||
|
return interfaceTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeDefinition safeGetSuperClass(final TypeDefinition typeDefinition) {
|
||||||
|
try {
|
||||||
|
return typeDefinition.getSuperClass();
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.debug(
|
||||||
|
"{} trying to get super class for target {}: {}",
|
||||||
|
e.getClass().getSimpleName(),
|
||||||
|
safeTypeDefinitionName(typeDefinition),
|
||||||
|
e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "safeHasSuperType(" + matcher + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,12 @@
|
||||||
package datadog.trace.agent.tooling.context;
|
package datadog.trace.agent.tooling.context;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
|
||||||
import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER;
|
import static datadog.trace.agent.tooling.ClassLoaderMatcher.BOOTSTRAP_CLASSLOADER;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import datadog.trace.agent.tooling.HelperInjector;
|
import datadog.trace.agent.tooling.HelperInjector;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
import datadog.trace.agent.tooling.Instrumenter.Default;
|
||||||
import datadog.trace.agent.tooling.Utils;
|
import datadog.trace.agent.tooling.Utils;
|
||||||
import datadog.trace.api.Config;
|
import datadog.trace.api.Config;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
|
@ -20,6 +19,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -126,8 +126,8 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
|
||||||
@Override
|
@Override
|
||||||
public AgentBuilder.Identified.Extendable instrumentationTransformer(
|
public AgentBuilder.Identified.Extendable instrumentationTransformer(
|
||||||
AgentBuilder.Identified.Extendable builder) {
|
AgentBuilder.Identified.Extendable builder) {
|
||||||
if (instrumenter.contextStore().size() > 0) {
|
if (!instrumenter.contextStore().isEmpty()) {
|
||||||
/**
|
/*
|
||||||
* Install transformer that rewrites accesses to context store with specialized bytecode that
|
* Install transformer that rewrites accesses to context store with specialized bytecode that
|
||||||
* invokes appropriate storage implementation.
|
* invokes appropriate storage implementation.
|
||||||
*/
|
*/
|
||||||
|
@ -303,14 +303,14 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
|
||||||
|
|
||||||
private AgentBuilder.Identified.Extendable injectHelpersIntoBootstrapClassloader(
|
private AgentBuilder.Identified.Extendable injectHelpersIntoBootstrapClassloader(
|
||||||
AgentBuilder.Identified.Extendable builder) {
|
AgentBuilder.Identified.Extendable builder) {
|
||||||
/**
|
/*
|
||||||
* We inject into bootstrap classloader because field accessor interfaces are needed by context
|
* We inject into bootstrap classloader because field accessor interfaces are needed by context
|
||||||
* store implementations. Unfortunately this forces us to remove stored type checking because
|
* store implementations. Unfortunately this forces us to remove stored type checking because
|
||||||
* actual classes may not be available at this point.
|
* actual classes may not be available at this point.
|
||||||
*/
|
*/
|
||||||
builder = builder.transform(fieldAccessorInterfacesInjector);
|
builder = builder.transform(fieldAccessorInterfacesInjector);
|
||||||
|
|
||||||
/**
|
/*
|
||||||
* We inject context store implementation into bootstrap classloader because same implementation
|
* We inject context store implementation into bootstrap classloader because same implementation
|
||||||
* may be used by different instrumentations and it has to use same static map in case of
|
* may be used by different instrumentations and it has to use same static map in case of
|
||||||
* fallback to map-backed storage.
|
* fallback to map-backed storage.
|
||||||
|
@ -341,35 +341,69 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Set of pairs (context holder, context class) for which we have matchers installed.
|
||||||
|
We use this to make sure we do not install matchers repeatedly for cases when same
|
||||||
|
context class is used by multiple instrumentations.
|
||||||
|
*/
|
||||||
|
private static final Set<Map.Entry<String, String>> INSTALLED_CONTEXT_MATCHERS = new HashSet<>();
|
||||||
|
|
||||||
|
/** Clear set that prevents multiple matchers for same context class */
|
||||||
|
public static void resetContextMatchers() {
|
||||||
|
synchronized (INSTALLED_CONTEXT_MATCHERS) {
|
||||||
|
INSTALLED_CONTEXT_MATCHERS.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AgentBuilder.Identified.Extendable additionalInstrumentation(
|
public AgentBuilder.Identified.Extendable additionalInstrumentation(
|
||||||
AgentBuilder.Identified.Extendable builder) {
|
AgentBuilder.Identified.Extendable builder) {
|
||||||
|
|
||||||
if (fieldInjectionEnabled) {
|
if (fieldInjectionEnabled) {
|
||||||
for (final Map.Entry<String, String> entry : instrumenter.contextStore().entrySet()) {
|
for (final Map.Entry<String, String> entry : instrumenter.contextStore().entrySet()) {
|
||||||
/**
|
/*
|
||||||
* For each context store defined in a current instrumentation we create an agent builder
|
* For each context store defined in a current instrumentation we create an agent builder
|
||||||
* that injects necessary fields.
|
* that injects necessary fields.
|
||||||
|
* Note: this synchronization should not have any impact on performance
|
||||||
|
* since this is done when agent builder is being made, it doesn't affect actual
|
||||||
|
* class transformation.
|
||||||
*/
|
*/
|
||||||
builder =
|
synchronized (INSTALLED_CONTEXT_MATCHERS) {
|
||||||
builder
|
// FIXME: This makes an assumption that class loader matchers for instrumenters that use
|
||||||
.type(
|
// same context classes should be the same - which seems reasonable, but is not checked.
|
||||||
safeHasSuperType(named(entry.getKey())).and(not(isInterface())),
|
// Addressing this properly requires some notion of 'compound intrumenters' which we
|
||||||
instrumenter.classLoaderMatcher())
|
// currently do not have.
|
||||||
.and(safeToInjectFieldsMatcher())
|
if (INSTALLED_CONTEXT_MATCHERS.contains(entry)) {
|
||||||
.transform(AgentBuilder.Transformer.NoOp.INSTANCE);
|
log.debug("Skipping builder for {} {}", instrumenter.getClass().getName(), entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
log.debug("Making builder for {} {}", instrumenter.getClass().getName(), entry);
|
||||||
* We inject helpers here as well as when instrumentation is applied to ensure that helpers
|
INSTALLED_CONTEXT_MATCHERS.add(entry);
|
||||||
* are present even if instrumented classes are not loaded, but classes with state fields
|
|
||||||
* added are loaded (e.g. sun.net.www.protocol.https.HttpsURLConnectionImpl).
|
|
||||||
*/
|
|
||||||
builder = injectHelpersIntoBootstrapClassloader(builder);
|
|
||||||
|
|
||||||
builder =
|
/*
|
||||||
builder.transform(
|
* For each context store defined in a current instrumentation we create an agent builder
|
||||||
getTransformerForASMVisitor(
|
* that injects necessary fields.
|
||||||
getFieldInjectionVisitor(entry.getKey(), entry.getValue())));
|
*/
|
||||||
|
builder =
|
||||||
|
builder
|
||||||
|
.type(safeHasSuperType(named(entry.getKey())), instrumenter.classLoaderMatcher())
|
||||||
|
.and(safeToInjectFieldsMatcher())
|
||||||
|
.and(Default.NOT_DECORATOR_MATCHER)
|
||||||
|
.transform(AgentBuilder.Transformer.NoOp.INSTANCE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We inject helpers here as well as when instrumentation is applied to ensure that
|
||||||
|
* helpers are present even if instrumented classes are not loaded, but classes with state
|
||||||
|
* fields added are loaded (e.g. sun.net.www.protocol.https.HttpsURLConnectionImpl).
|
||||||
|
*/
|
||||||
|
builder = injectHelpersIntoBootstrapClassloader(builder);
|
||||||
|
|
||||||
|
builder =
|
||||||
|
builder.transform(
|
||||||
|
getTransformerForASMVisitor(
|
||||||
|
getFieldInjectionVisitor(entry.getKey(), entry.getValue())));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
|
@ -384,7 +418,7 @@ public class FieldBackedProvider implements InstrumentationContextProvider {
|
||||||
final JavaModule module,
|
final JavaModule module,
|
||||||
final Class<?> classBeingRedefined,
|
final Class<?> classBeingRedefined,
|
||||||
final ProtectionDomain protectionDomain) {
|
final ProtectionDomain protectionDomain) {
|
||||||
/**
|
/*
|
||||||
* The idea here is that we can add fields if class is just being loaded
|
* The idea here is that we can add fields if class is just being loaded
|
||||||
* (classBeingRedefined == null) and we have to add same fields again if class we added
|
* (classBeingRedefined == null) and we have to add same fields again if class we added
|
||||||
* fields before is being transformed again. Note: here we assume that Class#getInterfaces()
|
* fields before is being transformed again. Note: here we assume that Class#getInterfaces()
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package datadog.trace.agent.tooling.context;
|
||||||
|
|
||||||
|
import net.bytebuddy.agent.builder.AgentBuilder.Identified.Extendable;
|
||||||
|
|
||||||
|
public class NoopContextProvider implements InstrumentationContextProvider {
|
||||||
|
|
||||||
|
public static NoopContextProvider INSTANCE = new NoopContextProvider();
|
||||||
|
|
||||||
|
private NoopContextProvider() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Extendable instrumentationTransformer(final Extendable builder) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Extendable additionalInstrumentation(final Extendable builder) {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,9 +22,8 @@ import net.bytebuddy.pool.TypePool;
|
||||||
|
|
||||||
/** Matches a set of references against a classloader. */
|
/** Matches a set of references against a classloader. */
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ReferenceMatcher
|
public final class ReferenceMatcher implements WeakMap.ValueSupplier<ClassLoader, Boolean> {
|
||||||
implements WeakMap.ValueSupplier<ClassLoader, List<Reference.Mismatch>> {
|
private final WeakMap<ClassLoader, Boolean> mismatchCache = newWeakMap();
|
||||||
private final WeakMap<ClassLoader, List<Reference.Mismatch>> mismatchCache = newWeakMap();
|
|
||||||
private final Reference[] references;
|
private final Reference[] references;
|
||||||
private final Set<String> helperClassNames;
|
private final Set<String> helperClassNames;
|
||||||
|
|
||||||
|
@ -42,18 +41,12 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Matcher used by ByteBuddy. Fails fast and only caches empty results, or complete results
|
||||||
|
*
|
||||||
* @param loader Classloader to validate against (or null for bootstrap)
|
* @param loader Classloader to validate against (or null for bootstrap)
|
||||||
* @return true if all references match the classpath of loader
|
* @return true if all references match the classpath of loader
|
||||||
*/
|
*/
|
||||||
public boolean matches(final ClassLoader loader) {
|
public boolean matches(ClassLoader loader) {
|
||||||
return getMismatchedReferenceSources(loader).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param loader Classloader to validate against (or null for bootstrap)
|
|
||||||
* @return A list of all mismatches between this ReferenceMatcher and loader's classpath.
|
|
||||||
*/
|
|
||||||
public List<Reference.Mismatch> getMismatchedReferenceSources(ClassLoader loader) {
|
|
||||||
if (loader == BOOTSTRAP_LOADER) {
|
if (loader == BOOTSTRAP_LOADER) {
|
||||||
loader = Utils.getBootstrapProxy();
|
loader = Utils.getBootstrapProxy();
|
||||||
}
|
}
|
||||||
|
@ -62,8 +55,32 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Mismatch> get(final ClassLoader loader) {
|
public Boolean get(final ClassLoader loader) {
|
||||||
final List<Mismatch> mismatches = new ArrayList<>(0);
|
for (final Reference reference : references) {
|
||||||
|
// Don't reference-check helper classes.
|
||||||
|
// They will be injected by the instrumentation's HelperInjector.
|
||||||
|
if (!helperClassNames.contains(reference.getClassName())) {
|
||||||
|
if (!checkMatch(reference, loader).isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the full list of mismatches. Used in debug contexts only
|
||||||
|
*
|
||||||
|
* @param loader Classloader to validate against (or null for bootstrap)
|
||||||
|
* @return A list of all mismatches between this ReferenceMatcher and loader's classpath.
|
||||||
|
*/
|
||||||
|
public List<Reference.Mismatch> getMismatchedReferenceSources(ClassLoader loader) {
|
||||||
|
if (loader == BOOTSTRAP_LOADER) {
|
||||||
|
loader = Utils.getBootstrapProxy();
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<Mismatch> mismatches = new ArrayList<>();
|
||||||
|
|
||||||
for (final Reference reference : references) {
|
for (final Reference reference : references) {
|
||||||
// Don't reference-check helper classes.
|
// Don't reference-check helper classes.
|
||||||
|
@ -82,11 +99,12 @@ public class ReferenceMatcher
|
||||||
* @param loader
|
* @param loader
|
||||||
* @return A list of mismatched sources. A list of size 0 means the reference matches the class.
|
* @return A list of mismatched sources. A list of size 0 means the reference matches the class.
|
||||||
*/
|
*/
|
||||||
public static List<Reference.Mismatch> checkMatch(Reference reference, ClassLoader loader) {
|
private static List<Reference.Mismatch> checkMatch(
|
||||||
|
final Reference reference, final ClassLoader loader) {
|
||||||
final TypePool typePool =
|
final TypePool typePool =
|
||||||
AgentTooling.poolStrategy()
|
AgentTooling.poolStrategy()
|
||||||
.typePool(AgentTooling.locationStrategy().classFileLocator(loader), loader);
|
.typePool(AgentTooling.locationStrategy().classFileLocator(loader), loader);
|
||||||
final List<Mismatch> mismatches = new ArrayList<>(0);
|
final List<Mismatch> mismatches = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
final TypePool.Resolution resolution =
|
final TypePool.Resolution resolution =
|
||||||
typePool.describe(Utils.getClassName(reference.getClassName()));
|
typePool.describe(Utils.getClassName(reference.getClassName()));
|
||||||
|
@ -96,7 +114,7 @@ public class ReferenceMatcher
|
||||||
reference.getSources().toArray(new Source[0]), reference.getClassName()));
|
reference.getSources().toArray(new Source[0]), reference.getClassName()));
|
||||||
}
|
}
|
||||||
return checkMatch(reference, resolution.resolve());
|
return checkMatch(reference, resolution.resolve());
|
||||||
} catch (Exception e) {
|
} catch (final Exception e) {
|
||||||
if (e.getMessage().startsWith("Cannot resolve type description for ")) {
|
if (e.getMessage().startsWith("Cannot resolve type description for ")) {
|
||||||
// bytebuddy throws an illegal state exception with this message if it cannot resolve types
|
// bytebuddy throws an illegal state exception with this message if it cannot resolve types
|
||||||
// TODO: handle missing type resolutions without catching bytebuddy's exceptions
|
// TODO: handle missing type resolutions without catching bytebuddy's exceptions
|
||||||
|
@ -112,10 +130,10 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Reference.Mismatch> checkMatch(
|
public static List<Reference.Mismatch> checkMatch(
|
||||||
Reference reference, TypeDescription typeOnClasspath) {
|
final Reference reference, final TypeDescription typeOnClasspath) {
|
||||||
final List<Mismatch> mismatches = new ArrayList<>(0);
|
final List<Mismatch> mismatches = new ArrayList<>();
|
||||||
|
|
||||||
for (Reference.Flag flag : reference.getFlags()) {
|
for (final Reference.Flag flag : reference.getFlags()) {
|
||||||
if (!flag.matches(typeOnClasspath.getModifiers())) {
|
if (!flag.matches(typeOnClasspath.getModifiers())) {
|
||||||
final String desc = reference.getClassName();
|
final String desc = reference.getClassName();
|
||||||
mismatches.add(
|
mismatches.add(
|
||||||
|
@ -127,8 +145,8 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Reference.Field fieldRef : reference.getFields()) {
|
for (final Reference.Field fieldRef : reference.getFields()) {
|
||||||
FieldDescription.InDefinedShape fieldDescription = findField(fieldRef, typeOnClasspath);
|
final FieldDescription.InDefinedShape fieldDescription = findField(fieldRef, typeOnClasspath);
|
||||||
if (fieldDescription == null) {
|
if (fieldDescription == null) {
|
||||||
mismatches.add(
|
mismatches.add(
|
||||||
new Reference.Mismatch.MissingField(
|
new Reference.Mismatch.MissingField(
|
||||||
|
@ -137,7 +155,7 @@ public class ReferenceMatcher
|
||||||
fieldRef.getName(),
|
fieldRef.getName(),
|
||||||
fieldRef.getType().getInternalName()));
|
fieldRef.getType().getInternalName()));
|
||||||
} else {
|
} else {
|
||||||
for (Reference.Flag flag : fieldRef.getFlags()) {
|
for (final Reference.Flag flag : fieldRef.getFlags()) {
|
||||||
if (!flag.matches(fieldDescription.getModifiers())) {
|
if (!flag.matches(fieldDescription.getModifiers())) {
|
||||||
final String desc =
|
final String desc =
|
||||||
reference.getClassName()
|
reference.getClassName()
|
||||||
|
@ -155,7 +173,7 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Reference.Method methodRef : reference.getMethods()) {
|
for (final Reference.Method methodRef : reference.getMethods()) {
|
||||||
final MethodDescription.InDefinedShape methodDescription =
|
final MethodDescription.InDefinedShape methodDescription =
|
||||||
findMethod(methodRef, typeOnClasspath);
|
findMethod(methodRef, typeOnClasspath);
|
||||||
if (methodDescription == null) {
|
if (methodDescription == null) {
|
||||||
|
@ -165,7 +183,7 @@ public class ReferenceMatcher
|
||||||
methodRef.getName(),
|
methodRef.getName(),
|
||||||
methodRef.getDescriptor()));
|
methodRef.getDescriptor()));
|
||||||
} else {
|
} else {
|
||||||
for (Reference.Flag flag : methodRef.getFlags()) {
|
for (final Reference.Flag flag : methodRef.getFlags()) {
|
||||||
if (!flag.matches(methodDescription.getModifiers())) {
|
if (!flag.matches(methodDescription.getModifiers())) {
|
||||||
final String desc =
|
final String desc =
|
||||||
reference.getClassName() + "#" + methodRef.getName() + methodRef.getDescriptor();
|
reference.getClassName() + "#" + methodRef.getName() + methodRef.getDescriptor();
|
||||||
|
@ -184,8 +202,8 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FieldDescription.InDefinedShape findField(
|
private static FieldDescription.InDefinedShape findField(
|
||||||
Reference.Field fieldRef, TypeDescription typeOnClasspath) {
|
final Reference.Field fieldRef, final TypeDescription typeOnClasspath) {
|
||||||
for (FieldDescription.InDefinedShape fieldType : typeOnClasspath.getDeclaredFields()) {
|
for (final FieldDescription.InDefinedShape fieldType : typeOnClasspath.getDeclaredFields()) {
|
||||||
if (fieldType.getName().equals(fieldRef.getName())
|
if (fieldType.getName().equals(fieldRef.getName())
|
||||||
&& fieldType
|
&& fieldType
|
||||||
.getType()
|
.getType()
|
||||||
|
@ -196,14 +214,14 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeOnClasspath.getSuperClass() != null) {
|
if (typeOnClasspath.getSuperClass() != null) {
|
||||||
FieldDescription.InDefinedShape fieldOnSupertype =
|
final FieldDescription.InDefinedShape fieldOnSupertype =
|
||||||
findField(fieldRef, typeOnClasspath.getSuperClass().asErasure());
|
findField(fieldRef, typeOnClasspath.getSuperClass().asErasure());
|
||||||
if (fieldOnSupertype != null) {
|
if (fieldOnSupertype != null) {
|
||||||
return fieldOnSupertype;
|
return fieldOnSupertype;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
|
for (final TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
|
||||||
FieldDescription.InDefinedShape fieldOnSupertype =
|
final FieldDescription.InDefinedShape fieldOnSupertype =
|
||||||
findField(fieldRef, interfaceType.asErasure());
|
findField(fieldRef, interfaceType.asErasure());
|
||||||
if (fieldOnSupertype != null) {
|
if (fieldOnSupertype != null) {
|
||||||
return fieldOnSupertype;
|
return fieldOnSupertype;
|
||||||
|
@ -213,8 +231,8 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MethodDescription.InDefinedShape findMethod(
|
private static MethodDescription.InDefinedShape findMethod(
|
||||||
Reference.Method methodRef, TypeDescription typeOnClasspath) {
|
final Reference.Method methodRef, final TypeDescription typeOnClasspath) {
|
||||||
for (MethodDescription.InDefinedShape methodDescription :
|
for (final MethodDescription.InDefinedShape methodDescription :
|
||||||
typeOnClasspath.getDeclaredMethods()) {
|
typeOnClasspath.getDeclaredMethods()) {
|
||||||
if (methodDescription.getInternalName().equals(methodRef.getName())
|
if (methodDescription.getInternalName().equals(methodRef.getName())
|
||||||
&& methodDescription.getDescriptor().equals(methodRef.getDescriptor())) {
|
&& methodDescription.getDescriptor().equals(methodRef.getDescriptor())) {
|
||||||
|
@ -222,14 +240,14 @@ public class ReferenceMatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeOnClasspath.getSuperClass() != null) {
|
if (typeOnClasspath.getSuperClass() != null) {
|
||||||
MethodDescription.InDefinedShape methodOnSupertype =
|
final MethodDescription.InDefinedShape methodOnSupertype =
|
||||||
findMethod(methodRef, typeOnClasspath.getSuperClass().asErasure());
|
findMethod(methodRef, typeOnClasspath.getSuperClass().asErasure());
|
||||||
if (methodOnSupertype != null) {
|
if (methodOnSupertype != null) {
|
||||||
return methodOnSupertype;
|
return methodOnSupertype;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
|
for (final TypeDescription.Generic interfaceType : typeOnClasspath.getInterfaces()) {
|
||||||
MethodDescription.InDefinedShape methodOnSupertype =
|
final MethodDescription.InDefinedShape methodOnSupertype =
|
||||||
findMethod(methodRef, interfaceType.asErasure());
|
findMethod(methodRef, interfaceType.asErasure());
|
||||||
if (methodOnSupertype != null) {
|
if (methodOnSupertype != null) {
|
||||||
return methodOnSupertype;
|
return methodOnSupertype;
|
||||||
|
|
|
@ -83,7 +83,6 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
|
||||||
|
|
||||||
then:
|
then:
|
||||||
if (conn) {
|
if (conn) {
|
||||||
1 * span.setTag(Tags.PEER_HOSTNAME.key, "test-host")
|
|
||||||
1 * span.setTag(Tags.PEER_PORT.key, 555)
|
1 * span.setTag(Tags.PEER_PORT.key, 555)
|
||||||
if (ipv4) {
|
if (ipv4) {
|
||||||
1 * span.setTag(Tags.PEER_HOST_IPV4.key, "10.0.0.1")
|
1 * span.setTag(Tags.PEER_HOST_IPV4.key, "10.0.0.1")
|
||||||
|
@ -96,9 +95,9 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
|
||||||
where:
|
where:
|
||||||
ipv4 | conn
|
ipv4 | conn
|
||||||
null | null
|
null | null
|
||||||
null | [host: "test-host", ip: null, port: 555]
|
null | [ip: null, port: 555]
|
||||||
true | [host: "test-host", ip: "10.0.0.1", port: 555]
|
true | [ip: "10.0.0.1", port: 555]
|
||||||
false | [host: "test-host", ip: "3ffe:1900:4545:3:200:f8ff:fe21:67cf", port: 555]
|
false | [ip: "3ffe:1900:4545:3:200:f8ff:fe21:67cf", port: 555]
|
||||||
}
|
}
|
||||||
|
|
||||||
def "test onResponse"() {
|
def "test onResponse"() {
|
||||||
|
@ -173,11 +172,6 @@ class HttpServerDecoratorTest extends ServerDecoratorTest {
|
||||||
return m.url
|
return m.url
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String peerHostname(Map m) {
|
|
||||||
return m.host
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String peerHostIP(Map m) {
|
protected String peerHostIP(Map m) {
|
||||||
return m.ip
|
return m.ip
|
||||||
|
|
|
@ -33,6 +33,11 @@ class ClassLoaderMatcherTest extends DDSpecification {
|
||||||
!ClassLoaderMatcher.skipClassLoader().matches(null)
|
!ClassLoaderMatcher.skipClassLoader().matches(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "DatadogClassLoader class name is hardcoded in ClassLoaderMatcher"() {
|
||||||
|
expect:
|
||||||
|
DatadogClassLoader.name == "datadog.trace.bootstrap.DatadogClassLoader"
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A URLClassloader which only delegates java.* classes
|
* A URLClassloader which only delegates java.* classes
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -3,7 +3,7 @@ package datadog.trace.agent.test
|
||||||
import ch.qos.logback.classic.Level
|
import ch.qos.logback.classic.Level
|
||||||
import ch.qos.logback.classic.Logger
|
import ch.qos.logback.classic.Logger
|
||||||
import ch.qos.logback.core.read.ListAppender
|
import ch.qos.logback.core.read.ListAppender
|
||||||
import datadog.trace.agent.tooling.ExceptionHandlers
|
import datadog.trace.agent.tooling.bytebuddy.ExceptionHandlers
|
||||||
import datadog.trace.bootstrap.ExceptionLogger
|
import datadog.trace.bootstrap.ExceptionLogger
|
||||||
import datadog.trace.util.test.DDSpecification
|
import datadog.trace.util.test.DDSpecification
|
||||||
import net.bytebuddy.agent.ByteBuddyAgent
|
import net.bytebuddy.agent.ByteBuddyAgent
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package datadog.trace.agent.test
|
package datadog.trace.agent.test
|
||||||
|
|
||||||
import datadog.trace.agent.tooling.DDLocationStrategy
|
import datadog.trace.agent.tooling.bytebuddy.DDLocationStrategy
|
||||||
import datadog.trace.util.test.DDSpecification
|
import datadog.trace.util.test.DDSpecification
|
||||||
import net.bytebuddy.agent.builder.AgentBuilder
|
import net.bytebuddy.agent.builder.AgentBuilder
|
||||||
import spock.lang.Shared
|
import spock.lang.Shared
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package datadog.trace.agent.tooling
|
package datadog.trace.agent.tooling
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.DDCachingPoolStrategy
|
||||||
import datadog.trace.util.test.DDSpecification
|
import datadog.trace.util.test.DDSpecification
|
||||||
import net.bytebuddy.description.type.TypeDescription
|
import net.bytebuddy.description.type.TypeDescription
|
||||||
import net.bytebuddy.dynamic.ClassFileLocator
|
import net.bytebuddy.dynamic.ClassFileLocator
|
||||||
|
@ -204,7 +205,7 @@ class CacheProviderTest extends DDSpecification {
|
||||||
}
|
}
|
||||||
|
|
||||||
static newClassLoader() {
|
static newClassLoader() {
|
||||||
return new URLClassLoader([] as URL[], (ClassLoader)null)
|
return new URLClassLoader([] as URL[], (ClassLoader) null)
|
||||||
}
|
}
|
||||||
|
|
||||||
static newLocator() {
|
static newLocator() {
|
||||||
|
@ -215,7 +216,8 @@ class CacheProviderTest extends DDSpecification {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void close() throws IOException {}
|
void close() throws IOException {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
|
||||||
|
import datadog.trace.util.test.DDSpecification
|
||||||
|
import net.bytebuddy.description.type.TypeDescription
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named
|
||||||
|
|
||||||
|
class ExtendsClassMatcherTest extends DDSpecification {
|
||||||
|
|
||||||
|
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
|
||||||
|
expect:
|
||||||
|
extendsClass(matcher).matches(argument) == result
|
||||||
|
|
||||||
|
where:
|
||||||
|
matcherClass | type | result
|
||||||
|
A | B | false
|
||||||
|
A | F | false
|
||||||
|
G | F | false
|
||||||
|
F | F | true
|
||||||
|
F | G | true
|
||||||
|
|
||||||
|
matcher = named(matcherClass.name)
|
||||||
|
argument = TypeDescription.ForLoadedType.of(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test traversal exceptions"() {
|
||||||
|
setup:
|
||||||
|
def type = Mock(TypeDescription)
|
||||||
|
def typeGeneric = Mock(TypeDescription.Generic)
|
||||||
|
def matcher = extendsClass(named(Object.name))
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(type)
|
||||||
|
|
||||||
|
then:
|
||||||
|
!result // default to false
|
||||||
|
noExceptionThrown()
|
||||||
|
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
|
||||||
|
1 * type.asGenericType() >> typeGeneric
|
||||||
|
1 * type.getTypeName() >> "type-name"
|
||||||
|
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
|
||||||
|
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
|
||||||
|
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
|
||||||
|
0 * _
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.E
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
|
||||||
|
import datadog.trace.util.test.DDSpecification
|
||||||
|
import net.bytebuddy.description.type.TypeDescription
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named
|
||||||
|
|
||||||
|
class HasInterfaceMatcherTest extends DDSpecification {
|
||||||
|
|
||||||
|
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
|
||||||
|
expect:
|
||||||
|
hasInterface(matcher).matches(argument) == result
|
||||||
|
|
||||||
|
where:
|
||||||
|
matcherClass | type | result
|
||||||
|
A | A | true
|
||||||
|
A | B | true
|
||||||
|
B | A | false
|
||||||
|
A | E | true
|
||||||
|
A | F | true
|
||||||
|
A | G | true
|
||||||
|
F | A | false
|
||||||
|
F | F | false
|
||||||
|
F | G | false
|
||||||
|
|
||||||
|
matcher = named(matcherClass.name)
|
||||||
|
argument = TypeDescription.ForLoadedType.of(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test traversal exceptions"() {
|
||||||
|
setup:
|
||||||
|
def type = Mock(TypeDescription)
|
||||||
|
def typeGeneric = Mock(TypeDescription.Generic)
|
||||||
|
def matcher = implementsInterface(named(Object.name))
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(type)
|
||||||
|
|
||||||
|
then:
|
||||||
|
!result // default to false
|
||||||
|
noExceptionThrown()
|
||||||
|
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
|
||||||
|
1 * type.isInterface() >> true
|
||||||
|
1 * type.asGenericType() >> typeGeneric
|
||||||
|
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
|
||||||
|
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
|
||||||
|
1 * type.getInterfaces() >> { throw new Exception("getInterfaces exception") }
|
||||||
|
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
|
||||||
|
2 * type.getTypeName() >> "type-name"
|
||||||
|
0 * _
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.C
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.TracedClass
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.UntracedClass
|
||||||
|
import datadog.trace.api.Trace
|
||||||
|
import datadog.trace.util.test.DDSpecification
|
||||||
|
import net.bytebuddy.description.method.MethodDescription
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasSuperMethod
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.none
|
||||||
|
|
||||||
|
class HasSuperMethodMatcherTest extends DDSpecification {
|
||||||
|
|
||||||
|
def "test matcher #type.simpleName #method"() {
|
||||||
|
expect:
|
||||||
|
hasSuperMethod(isAnnotatedWith(Trace)).matches(argument) == result
|
||||||
|
|
||||||
|
where:
|
||||||
|
type | method | result
|
||||||
|
A | "a" | false
|
||||||
|
B | "b" | true
|
||||||
|
C | "c" | false
|
||||||
|
F | "f" | true
|
||||||
|
G | "g" | false
|
||||||
|
TracedClass | "a" | true
|
||||||
|
UntracedClass | "a" | false
|
||||||
|
UntracedClass | "b" | true
|
||||||
|
|
||||||
|
argument = new MethodDescription.ForLoadedMethod(type.getDeclaredMethod(method))
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test constructor never matches"() {
|
||||||
|
setup:
|
||||||
|
def method = Mock(MethodDescription)
|
||||||
|
def matcher = hasSuperMethod(none())
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(method)
|
||||||
|
|
||||||
|
then:
|
||||||
|
!result
|
||||||
|
1 * method.isConstructor() >> true
|
||||||
|
0 * _
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test traversal exceptions"() {
|
||||||
|
setup:
|
||||||
|
def method = Mock(MethodDescription)
|
||||||
|
def matcher = hasSuperMethod(none())
|
||||||
|
def sigToken = new MethodDescription.ForLoadedMethod(A.getDeclaredMethod("a")).asSignatureToken()
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(method)
|
||||||
|
|
||||||
|
then:
|
||||||
|
!result // default to false
|
||||||
|
1 * method.isConstructor() >> false
|
||||||
|
1 * method.asSignatureToken() >> sigToken
|
||||||
|
1 * method.getDeclaringType() >> null
|
||||||
|
0 * _
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.E
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
|
||||||
|
import datadog.trace.util.test.DDSpecification
|
||||||
|
import net.bytebuddy.description.type.TypeDescription
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named
|
||||||
|
|
||||||
|
class ImplementsInterfaceMatcherTest extends DDSpecification {
|
||||||
|
|
||||||
|
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
|
||||||
|
expect:
|
||||||
|
implementsInterface(matcher).matches(argument) == result
|
||||||
|
|
||||||
|
where:
|
||||||
|
matcherClass | type | result
|
||||||
|
A | A | false
|
||||||
|
A | B | false
|
||||||
|
B | A | false
|
||||||
|
A | E | false
|
||||||
|
A | F | true
|
||||||
|
A | G | true
|
||||||
|
F | A | false
|
||||||
|
F | F | false
|
||||||
|
F | G | false
|
||||||
|
|
||||||
|
matcher = named(matcherClass.name)
|
||||||
|
argument = TypeDescription.ForLoadedType.of(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test traversal exceptions"() {
|
||||||
|
setup:
|
||||||
|
def type = Mock(TypeDescription)
|
||||||
|
def typeGeneric = Mock(TypeDescription.Generic)
|
||||||
|
def matcher = implementsInterface(named(Object.name))
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(type)
|
||||||
|
|
||||||
|
then:
|
||||||
|
!result // default to false
|
||||||
|
noExceptionThrown()
|
||||||
|
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
|
||||||
|
1 * type.isInterface() >> true
|
||||||
|
1 * type.asGenericType() >> typeGeneric
|
||||||
|
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
|
||||||
|
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
|
||||||
|
1 * type.getInterfaces() >> { throw new Exception("getInterfaces exception") }
|
||||||
|
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
|
||||||
|
2 * type.getTypeName() >> "type-name"
|
||||||
|
0 * _
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher
|
||||||
|
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.A
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.B
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.E
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.F
|
||||||
|
import datadog.trace.agent.tooling.bytebuddy.matcher.testclasses.G
|
||||||
|
import datadog.trace.util.test.DDSpecification
|
||||||
|
import net.bytebuddy.description.type.TypeDescription
|
||||||
|
import net.bytebuddy.jar.asm.Opcodes
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.safeHasSuperType
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named
|
||||||
|
|
||||||
|
class SafeHasSuperTypeMatcherTest extends DDSpecification {
|
||||||
|
|
||||||
|
def "test matcher #matcherClass.simpleName -> #type.simpleName"() {
|
||||||
|
expect:
|
||||||
|
safeHasSuperType(matcher).matches(argument) == result
|
||||||
|
|
||||||
|
where:
|
||||||
|
matcherClass | type | result
|
||||||
|
A | A | false
|
||||||
|
A | B | false
|
||||||
|
B | A | false
|
||||||
|
A | E | false
|
||||||
|
A | F | true
|
||||||
|
B | G | true
|
||||||
|
F | A | false
|
||||||
|
F | F | true
|
||||||
|
F | G | true
|
||||||
|
|
||||||
|
matcher = named(matcherClass.name)
|
||||||
|
argument = TypeDescription.ForLoadedType.of(type)
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test traversal exceptions"() {
|
||||||
|
setup:
|
||||||
|
def type = Mock(TypeDescription)
|
||||||
|
def typeGeneric = Mock(TypeDescription.Generic)
|
||||||
|
def matcher = safeHasSuperType(named(Object.name))
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(type)
|
||||||
|
|
||||||
|
then:
|
||||||
|
!result // default to false
|
||||||
|
noExceptionThrown()
|
||||||
|
1 * type.getModifiers() >> Opcodes.ACC_ABSTRACT
|
||||||
|
1 * type.asGenericType() >> typeGeneric
|
||||||
|
1 * typeGeneric.asErasure() >> { throw new Exception("asErasure exception") }
|
||||||
|
1 * typeGeneric.getTypeName() >> "typeGeneric-name"
|
||||||
|
1 * type.getInterfaces() >> { throw new Exception("getInterfaces exception") }
|
||||||
|
1 * type.getSuperClass() >> { throw new Exception("getSuperClass exception") }
|
||||||
|
2 * type.getTypeName() >> "type-name"
|
||||||
|
0 * _
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher
|
||||||
|
|
||||||
|
import datadog.trace.util.test.DDSpecification
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.failSafe
|
||||||
|
|
||||||
|
class SafeMatcherTest extends DDSpecification {
|
||||||
|
|
||||||
|
def mockMatcher = Mock(ElementMatcher)
|
||||||
|
|
||||||
|
def "test matcher"() {
|
||||||
|
setup:
|
||||||
|
def matcher = failSafe(mockMatcher, "test")
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(new Object())
|
||||||
|
|
||||||
|
then:
|
||||||
|
1 * mockMatcher.matches(_) >> match
|
||||||
|
result == match
|
||||||
|
|
||||||
|
where:
|
||||||
|
match << [true, false]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "test matcher exception"() {
|
||||||
|
setup:
|
||||||
|
def matcher = failSafe(mockMatcher, "test")
|
||||||
|
|
||||||
|
when:
|
||||||
|
def result = matcher.matches(new Object())
|
||||||
|
|
||||||
|
then:
|
||||||
|
1 * mockMatcher.matches(_) >> { throw new Exception("matcher exception") }
|
||||||
|
0 * _
|
||||||
|
noExceptionThrown()
|
||||||
|
!result // default to false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
public interface A {
|
||||||
|
void a();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
import datadog.trace.api.Trace;
|
||||||
|
|
||||||
|
public interface B extends A {
|
||||||
|
@Trace
|
||||||
|
void b();
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
public interface C extends A, B {
|
||||||
|
void c();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
import datadog.trace.api.Trace;
|
||||||
|
|
||||||
|
public interface D extends A, B, C {
|
||||||
|
@Trace
|
||||||
|
void d();
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
public interface E extends B, C, D {
|
||||||
|
void e();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
import datadog.trace.api.Trace;
|
||||||
|
|
||||||
|
public abstract class F implements E {
|
||||||
|
@Trace
|
||||||
|
public abstract void f();
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
public abstract class G extends F {
|
||||||
|
public void g() {}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
import datadog.trace.api.Trace;
|
||||||
|
|
||||||
|
public class TracedClass extends UntracedClass {
|
||||||
|
@Trace
|
||||||
|
@Override
|
||||||
|
public void g() {}
|
||||||
|
|
||||||
|
@Trace
|
||||||
|
@Override
|
||||||
|
public void f() {}
|
||||||
|
|
||||||
|
@Trace
|
||||||
|
@Override
|
||||||
|
public void e() {}
|
||||||
|
|
||||||
|
@Trace
|
||||||
|
@Override
|
||||||
|
public void d() {}
|
||||||
|
|
||||||
|
@Trace
|
||||||
|
@Override
|
||||||
|
public void c() {}
|
||||||
|
|
||||||
|
@Trace
|
||||||
|
@Override
|
||||||
|
public void b() {}
|
||||||
|
|
||||||
|
@Trace
|
||||||
|
@Override
|
||||||
|
public void a() {}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package datadog.trace.agent.tooling.bytebuddy.matcher.testclasses;
|
||||||
|
|
||||||
|
public class UntracedClass extends G {
|
||||||
|
@Override
|
||||||
|
public void g() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void f() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void e() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void d() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void c() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void b() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void a() {}
|
||||||
|
}
|
|
@ -6,68 +6,44 @@ apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
jmh project(':dd-trace-api')
|
jmh project(':dd-trace-api')
|
||||||
jmh group: 'net.bytebuddy', name: 'byte-buddy-agent', version: '1.7.6'
|
jmh deps.bytebuddyagent
|
||||||
|
|
||||||
// Add a bunch of dependencies so instrumentation is not disabled.
|
|
||||||
jmh group: 'javax.jms', name: 'javax.jms-api', version: '2.0.1'
|
|
||||||
jmh group: 'javax.servlet', name: 'javax.servlet-api', version: '3.0.1'
|
|
||||||
jmh group: 'org.mongodb', name: 'mongo-java-driver', version: '3.4.2'
|
|
||||||
jmh group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.4.2'
|
|
||||||
jmh(group: 'com.amazonaws', name: 'aws-java-sdk', version: '1.11.119') {
|
|
||||||
exclude(module: 'httpclient')
|
|
||||||
exclude(module: 'jackson-databind')
|
|
||||||
exclude(module: 'jackson-dataformat-cbor')
|
|
||||||
}
|
|
||||||
jmh group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.6.0'
|
|
||||||
jmh group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.3'
|
|
||||||
jmh(group: 'com.datastax.cassandra', name: 'cassandra-driver-core', version: '3.2.0')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations.testRuntimeClasspath.dependencies.clear()
|
|
||||||
|
|
||||||
|
|
||||||
jmh {
|
jmh {
|
||||||
timeUnit = 'us' // Output time unit. Available time units are: [m, s, ms, us, ns].
|
timeUnit = 'ms' // Output time unit. Available time units are: [m, s, ms, us, ns].
|
||||||
benchmarkMode = ['thrpt', 'avgt']
|
benchmarkMode = ['avgt']
|
||||||
// timeOnIteration = '5s'
|
timeOnIteration = '20s'
|
||||||
iterations = 5 // Number of measurement iterations to do.
|
iterations = 1 // Number of measurement iterations to do.
|
||||||
fork = 1 // How many times to forks a single benchmark. Use 0 to disable forking altogether
|
fork = 1 // How many times to forks a single benchmark. Use 0 to disable forking altogether
|
||||||
// jvmArgs = ["-Dasdf=123"]
|
jvmArgs = ["-Ddd.jmxfetch.enabled=false", "-Ddd.writer.type=LoggingWriter"]
|
||||||
// jvmArgs = ["-javaagent:${project(':dd-java-agent').shadowJar.archivePath}"]
|
// jvmArgs += ["-XX:+UnlockDiagnosticVMOptions", "-XX:+DebugNonSafepoints", "-XX:StartFlightRecording=delay=5s,dumponexit=true,name=jmh-benchmark,filename=${rootDir}/dd-java-agent/benchmark/build/reports/jmh/jmh-benchmark.jfr"]
|
||||||
|
// jvmArgs += ["-agentpath:${rootDir}/dd-java-agent/benchmark/src/jmh/resources/libasyncProfiler.so=start,collapsed,file=${rootDir}/dd-java-agent/benchmark/build/reports/jmh/profiler.txt".toString()]
|
||||||
failOnError = true // Should JMH fail immediately if any benchmark had experienced the unrecoverable error?
|
failOnError = true // Should JMH fail immediately if any benchmark had experienced the unrecoverable error?
|
||||||
// warmup = '2s' // Time to spend at each warmup iteration.
|
warmup = '5s' // Time to spend at each warmup iteration.
|
||||||
// warmupIterations = 2 // Number of warmup iterations to do.
|
// warmupBatchSize = 10 // Warmup batch size: number of benchmark method calls per operation.
|
||||||
// warmupForks = 0 // How many warmup forks to make for a single benchmark. 0 to disable warmup forks.
|
warmupForks = 0 // How many warmup forks to make for a single benchmark. 0 to disable warmup forks.
|
||||||
|
warmupIterations = 1 // Number of warmup iterations to do.
|
||||||
|
|
||||||
// profilers = ['stack']
|
// profilers = ['stack:lines=5;detailLine=true;period=5;excludePackages=true']
|
||||||
// Use profilers to collect additional data. Supported profilers: [cl, comp, gc, stack, perf, perfnorm, perfasm, xperf, xperfasm, hs_cl, hs_comp, hs_gc, hs_rt, hs_thr]
|
// Use profilers to collect additional data. Supported profilers: [cl, comp, gc, stack, perf, perfnorm, perfasm, xperf, xperfasm, hs_cl, hs_comp, hs_gc, hs_rt, hs_thr]
|
||||||
|
|
||||||
// humanOutputFile = project.file("${project.buildDir}/reports/jmh/human.txt") // human-readable output file
|
// humanOutputFile = project.file("${project.buildDir}/reports/jmh/human.txt") // human-readable output file
|
||||||
// operationsPerInvocation = 10 // Operations per invocation.
|
// operationsPerInvocation = 10 // Operations per invocation.
|
||||||
// synchronizeIterations = false // Synchronize iterations?
|
// synchronizeIterations = false // Synchronize iterations?
|
||||||
timeout = '1s' // Timeout for benchmark iteration.
|
timeout = '5s' // Timeout for benchmark iteration.
|
||||||
includeTests = false
|
// includeTests = false
|
||||||
// Allows to include test sources into generate JMH jar, i.e. use it when benchmarks depend on the test classes.
|
// Allows to include test sources into generate JMH jar, i.e. use it when benchmarks depend on the test classes.
|
||||||
|
|
||||||
duplicateClassesStrategy = 'fail'
|
duplicateClassesStrategy = DuplicatesStrategy.EXCLUDE
|
||||||
jmhVersion = '1.20' // Specifies JMH version
|
jmhVersion = '1.23' // Specifies JMH version
|
||||||
}
|
}
|
||||||
|
|
||||||
// configured as a separate task since the 'jmh' task did not like adding a javaagent argument.
|
tasks.jmh.dependsOn project(':dd-java-agent').shadowJar
|
||||||
tasks.register("jmhAgent", JavaExec) {
|
|
||||||
classpath = files(project.jmhCompileGeneratedClasses.destinationDir)
|
|
||||||
classpath += sourceSets.jmh.runtimeClasspath
|
|
||||||
main = "org.openjdk.jmh.Main"
|
|
||||||
args += ["-tu", "us"]
|
|
||||||
args += ["-bm", "avgt"]
|
|
||||||
// args += ["-prof", "stack:lines=5;detailLine=true;period=5;excludePackages=true"]
|
|
||||||
args += ["-f", "1"]
|
|
||||||
args += ["-foe", "true"]
|
|
||||||
|
|
||||||
// args += ["-wi", "2"]
|
/*
|
||||||
// args += ["-i", "5"]
|
If using libasyncProfiler, use the following to generate nice svg flamegraphs.
|
||||||
dependsOn project.tasks.jmhCompileGeneratedClasses
|
sed '/unknown/d' dd-java-agent/benchmark/build/reports/jmh/profiler.txt | sed '/^thread_start/d' | sed '/not_walkable/d' > dd-java-agent/benchmark/build/reports/jmh/profiler-cleaned.txt
|
||||||
}
|
(using https://github.com/brendangregg/FlameGraph)
|
||||||
|
./flamegraph.pl --color=java dd-java-agent/benchmark/build/reports/jmh/profiler-cleaned.txt > dd-java-agent/benchmark/build/reports/jmh/jmh-master.svg
|
||||||
tasks.jmhAgent.dependsOn project(':dd-java-agent').shadowJar
|
*/
|
||||||
|
|
||||||
|
|
|
@ -4,49 +4,17 @@ import datadog.benchmark.classes.TracedClass;
|
||||||
import datadog.benchmark.classes.UntracedClass;
|
import datadog.benchmark.classes.UntracedClass;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
import java.lang.instrument.UnmodifiableClassException;
|
import java.lang.instrument.UnmodifiableClassException;
|
||||||
import java.lang.reflect.Field;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import net.bytebuddy.agent.ByteBuddyAgent;
|
import net.bytebuddy.agent.ByteBuddyAgent;
|
||||||
import org.openjdk.jmh.annotations.Benchmark;
|
import org.openjdk.jmh.annotations.Benchmark;
|
||||||
import org.openjdk.jmh.annotations.Fork;
|
import org.openjdk.jmh.annotations.Fork;
|
||||||
import org.openjdk.jmh.annotations.Scope;
|
import org.openjdk.jmh.annotations.Scope;
|
||||||
import org.openjdk.jmh.annotations.Setup;
|
|
||||||
import org.openjdk.jmh.annotations.State;
|
import org.openjdk.jmh.annotations.State;
|
||||||
import org.openjdk.jmh.annotations.TearDown;
|
|
||||||
|
|
||||||
public class ClassRetransformingBenchmark {
|
public class ClassRetransformingBenchmark {
|
||||||
public static final String BENCHMARK_HOME =
|
|
||||||
Paths.get(".").toAbsolutePath().normalize().toString();
|
|
||||||
|
|
||||||
static {
|
|
||||||
if (!BENCHMARK_HOME.endsWith("benchmark")) {
|
|
||||||
throw new IllegalArgumentException("Invalid Home directory: " + BENCHMARK_HOME);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@State(Scope.Benchmark)
|
@State(Scope.Benchmark)
|
||||||
public static class BenchmarkState {
|
public static class BenchmarkState {
|
||||||
private final Instrumentation inst = ByteBuddyAgent.install();
|
private final Instrumentation inst = ByteBuddyAgent.install();
|
||||||
|
|
||||||
@Setup
|
|
||||||
public void initializeInstrumentation() {
|
|
||||||
// loading TracedClass will initialize helper injection
|
|
||||||
TracedClass.class.getName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@TearDown
|
|
||||||
public void stopAgent() {
|
|
||||||
try {
|
|
||||||
final Class<?> gt = Class.forName("io.opentracing.util.GlobalTracer");
|
|
||||||
final Field tracerField = gt.getDeclaredField("tracer");
|
|
||||||
tracerField.setAccessible(true);
|
|
||||||
final Object tracer = tracerField.get(null);
|
|
||||||
final Method close = tracer.getClass().getMethod("close");
|
|
||||||
close.invoke(tracer);
|
|
||||||
} catch (final Exception e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Benchmark
|
@Benchmark
|
||||||
|
@ -60,54 +28,11 @@ public class ClassRetransformingBenchmark {
|
||||||
state.inst.retransformClasses(TracedClass.class);
|
state.inst.retransformClasses(TracedClass.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.2.jar")
|
@Fork(jvmArgsAppend = "-javaagent:/path/to/dd-java-agent-master.jar")
|
||||||
public static class WithAgent022 extends ClassRetransformingBenchmark {}
|
public static class WithAgentMaster extends ClassRetransformingBenchmark {}
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.4.jar")
|
@Fork(
|
||||||
public static class WithAgent024 extends ClassRetransformingBenchmark {}
|
jvmArgsAppend =
|
||||||
|
"-javaagent:/path/to/dd-trace-java/dd-java-agent/build/libs/dd-java-agent.jar")
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.6.jar")
|
|
||||||
public static class WithAgent026 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.7.jar")
|
|
||||||
public static class WithAgent027 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.8.jar")
|
|
||||||
public static class WithAgent028 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.9.jar")
|
|
||||||
public static class WithAgent029 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.10.jar")
|
|
||||||
public static class WithAgent0210 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.11.jar")
|
|
||||||
public static class WithAgent0211 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.2.12.jar")
|
|
||||||
public static class WithAgent0212 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.0.jar")
|
|
||||||
public static class WithAgent030 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.1.jar")
|
|
||||||
public static class WithAgent031 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.2.jar")
|
|
||||||
public static class WithAgent032 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.3.3.jar")
|
|
||||||
public static class WithAgent033 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.4.0.jar")
|
|
||||||
public static class WithAgent040 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.4.1.jar")
|
|
||||||
public static class WithAgent041 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:releases/dd-java-agent-0.5.0.jar")
|
|
||||||
public static class WithAgent050 extends ClassRetransformingBenchmark {}
|
|
||||||
|
|
||||||
@Fork(jvmArgsAppend = "-javaagent:../build/libs/dd-java-agent.jar")
|
|
||||||
public static class WithAgent extends ClassRetransformingBenchmark {}
|
public static class WithAgent extends ClassRetransformingBenchmark {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package datadog.benchmark.classes;
|
package datadog.benchmark.classes;
|
||||||
|
|
||||||
public interface C extends B {
|
public interface C extends A, B {
|
||||||
void c();
|
void c();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package datadog.benchmark.classes;
|
package datadog.benchmark.classes;
|
||||||
|
|
||||||
public interface D extends C {
|
public interface D extends A, B, C {
|
||||||
void d();
|
void d();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
package datadog.benchmark.classes;
|
package datadog.benchmark.classes;
|
||||||
|
|
||||||
public interface E extends D {
|
public interface E extends B, C, D {
|
||||||
void e();
|
void e();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
enableCustomAnnotationTracingOver: ["datadog.benchmark"]
|
|
|
@ -30,11 +30,6 @@ public class AkkaHttpServerDecorator
|
||||||
return new URI(httpRequest.uri().toString());
|
return new URI(httpRequest.uri().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String peerHostname(final HttpRequest httpRequest) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String peerHostIP(final HttpRequest httpRequest) {
|
protected String peerHostIP(final HttpRequest httpRequest) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package datadog.trace.instrumentation.apachehttpasyncclient;
|
package datadog.trace.instrumentation.apachehttpasyncclient;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
|
@ -40,7 +40,7 @@ public class ApacheHttpAsyncClientInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return safeHasSuperType(named("org.apache.http.nio.client.HttpAsyncClient"));
|
return implementsInterface(named("org.apache.http.nio.client.HttpAsyncClient"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package datadog.trace.instrumentation.apachehttpasyncclient;
|
package datadog.trace.instrumentation.apachehttpasyncclient;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
@ -31,7 +31,7 @@ public class ApacheHttpClientRedirectInstrumentation extends Instrumenter.Defaul
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return safeHasSuperType(named("org.apache.http.client.RedirectStrategy"));
|
return implementsInterface(named("org.apache.http.client.RedirectStrategy"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package datadog.trace.instrumentation.apachehttpclient;
|
package datadog.trace.instrumentation.apachehttpclient;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.apachehttpclient.ApacheHttpClientDecorator.DECORATE;
|
import static datadog.trace.instrumentation.apachehttpclient.ApacheHttpClientDecorator.DECORATE;
|
||||||
import static datadog.trace.instrumentation.apachehttpclient.HttpHeadersInjectAdapter.SETTER;
|
import static datadog.trace.instrumentation.apachehttpclient.HttpHeadersInjectAdapter.SETTER;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
|
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
@ -44,7 +43,7 @@ public class ApacheHttpClientInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.apache.http.client.HttpClient")));
|
return implementsInterface(named("org.apache.http.client.HttpClient"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -9,7 +9,6 @@ import datadog.trace.api.DDTags;
|
||||||
import datadog.trace.bootstrap.ContextStore;
|
import datadog.trace.bootstrap.ContextStore;
|
||||||
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
|
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
@ -102,8 +101,8 @@ public class AwsSdkClientDecorator extends HttpClientDecorator<Request, Response
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected URI url(final Request request) throws URISyntaxException {
|
protected URI url(final Request request) {
|
||||||
return new URI(request.getEndpoint().toString());
|
return request.getEndpoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package datadog.trace.instrumentation.aws.v0;
|
package datadog.trace.instrumentation.aws.v0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
@ -26,7 +27,8 @@ public final class RequestInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return safeHasSuperType(named("com.amazonaws.AmazonWebServiceRequest"));
|
return nameStartsWith("com.amazonaws.")
|
||||||
|
.and(extendsClass(named("com.amazonaws.AmazonWebServiceRequest")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package datadog.trace.instrumentation.aws.v2;
|
package datadog.trace.instrumentation.aws.v2;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
@ -23,8 +22,10 @@ public final class AwsClientInstrumentation extends AbstractAwsClientInstrumenta
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return safeHasSuperType(named("software.amazon.awssdk.core.client.builder.SdkClientBuilder"))
|
return nameStartsWith("software.amazon.awssdk.")
|
||||||
.and(not(isInterface()));
|
.and(
|
||||||
|
implementsInterface(
|
||||||
|
named("software.amazon.awssdk.core.client.builder.SdkClientBuilder")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package datadog.trace.instrumentation.aws.v2;
|
package datadog.trace.instrumentation.aws.v2;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeScope;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
@ -28,12 +27,14 @@ public final class AwsHttpClientInstrumentation extends AbstractAwsClientInstrum
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return safeHasSuperType(
|
return nameStartsWith("software.amazon.awssdk.")
|
||||||
named("software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage")
|
.and(
|
||||||
.or(
|
extendsClass(
|
||||||
named(
|
named(
|
||||||
"software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage")))
|
"software.amazon.awssdk.core.internal.http.pipeline.stages.MakeHttpRequestStage")
|
||||||
.and(not(isInterface()));
|
.or(
|
||||||
|
named(
|
||||||
|
"software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import datadog.trace.agent.test.AgentTestRunner
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
import datadog.trace.instrumentation.TestBean
|
|
||||||
import org.jboss.weld.environment.se.Weld
|
import org.jboss.weld.environment.se.Weld
|
||||||
import org.jboss.weld.environment.se.WeldContainer
|
import org.jboss.weld.environment.se.WeldContainer
|
||||||
import org.jboss.weld.environment.se.threading.RunnableDecorator
|
import org.jboss.weld.environment.se.threading.RunnableDecorator
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
package datadog.trace.instrumentation;
|
|
||||||
|
|
||||||
public class TestBean {
|
public class TestBean {
|
||||||
|
|
||||||
private String someField;
|
private String someField;
|
|
@ -0,0 +1 @@
|
||||||
|
apply from: "${rootDir}/gradle/java.gradle"
|
|
@ -1,5 +1,7 @@
|
||||||
apply from: "${rootDir}/gradle/java.gradle"
|
apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
testCompile project(':dd-java-agent:instrumentation:classloading')
|
||||||
|
|
||||||
testCompile group: 'org.jboss.modules', name: 'jboss-modules', version: '1.3.10.Final'
|
testCompile group: 'org.jboss.modules', name: 'jboss-modules', version: '1.3.10.Final'
|
||||||
}
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
|
import org.jboss.modules.ModuleFinder
|
||||||
|
import org.jboss.modules.ModuleIdentifier
|
||||||
|
import org.jboss.modules.ModuleLoadException
|
||||||
|
import org.jboss.modules.ModuleLoader
|
||||||
|
import org.jboss.modules.ModuleSpec
|
||||||
|
|
||||||
|
class JBossClassloadingTest extends AgentTestRunner {
|
||||||
|
def "delegates to bootstrap class loader for agent classes"() {
|
||||||
|
setup:
|
||||||
|
def moduleFinders = new ModuleFinder[1]
|
||||||
|
moduleFinders[0] = new ModuleFinder() {
|
||||||
|
@Override
|
||||||
|
ModuleSpec findModule(ModuleIdentifier identifier, ModuleLoader delegateLoader) throws ModuleLoadException {
|
||||||
|
return ModuleSpec.build(identifier).create()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def moduleLoader = new ModuleLoader(moduleFinders)
|
||||||
|
def moduleId = ModuleIdentifier.fromString("test")
|
||||||
|
def testModule = moduleLoader.loadModule(moduleId)
|
||||||
|
def classLoader = testModule.getClassLoader()
|
||||||
|
|
||||||
|
when:
|
||||||
|
Class<?> clazz
|
||||||
|
try {
|
||||||
|
clazz = Class.forName("datadog.trace.api.GlobalTracer", false, classLoader)
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
assert clazz != null
|
||||||
|
assert clazz.getClassLoader() == null
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
apply from: "${rootDir}/gradle/java.gradle"
|
apply from: "${rootDir}/gradle/java.gradle"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
testCompile project(':dd-java-agent:instrumentation:classloading')
|
||||||
|
|
||||||
// TODO: we should separate core and Eclipse tests at some point,
|
// TODO: we should separate core and Eclipse tests at some point,
|
||||||
// but right now core-specific tests are quite dump and are run with
|
// but right now core-specific tests are quite dump and are run with
|
|
@ -0,0 +1,68 @@
|
||||||
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
|
import org.apache.felix.framework.BundleWiringImpl
|
||||||
|
import org.eclipse.osgi.internal.debug.Debug
|
||||||
|
import org.eclipse.osgi.internal.framework.EquinoxConfiguration
|
||||||
|
import org.eclipse.osgi.internal.loader.BundleLoader
|
||||||
|
import org.eclipse.osgi.internal.loader.ModuleClassLoader
|
||||||
|
import org.eclipse.osgi.internal.loader.classpath.ClasspathManager
|
||||||
|
import org.eclipse.osgi.storage.BundleInfo
|
||||||
|
|
||||||
|
class OSGIClassloadingTest extends AgentTestRunner {
|
||||||
|
def "OSGI delegates to bootstrap class loader for agent classes"() {
|
||||||
|
when:
|
||||||
|
def clazz
|
||||||
|
if (args == 1) {
|
||||||
|
clazz = loader.loadClass("datadog.trace.api.GlobalTracer")
|
||||||
|
} else {
|
||||||
|
clazz = loader.loadClass("datadog.trace.api.GlobalTracer", false)
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
assert clazz != null
|
||||||
|
assert clazz.getClassLoader() == null
|
||||||
|
|
||||||
|
where:
|
||||||
|
loader | args
|
||||||
|
new TestClassLoader() | 1
|
||||||
|
new TestClassLoader() | 2
|
||||||
|
new BundleWiringImpl.BundleClassLoader(null, null, null) | 1
|
||||||
|
new BundleWiringImpl.BundleClassLoader(null, null, null) | 2
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TestClassLoader extends ModuleClassLoader {
|
||||||
|
|
||||||
|
TestClassLoader() {
|
||||||
|
super(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BundleInfo.Generation getGeneration() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Debug getDebug() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ClasspathManager getClasspathManager() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected EquinoxConfiguration getConfiguration() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
BundleLoader getBundleLoader() {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean isRegisteredAsParallel() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package datadog.trace.instrumentation.classloading;
|
||||||
|
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
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.bootstrap.CallDepthThreadLocalMap;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Some class loaders do not delegate to their parent, so classes in those class loaders
|
||||||
|
* will not be able to see classes in the bootstrap class loader.
|
||||||
|
*
|
||||||
|
* In particular, instrumentation on classes in those class loaders will not be able to see
|
||||||
|
* the shaded OpenTelemetry API classes in the bootstrap class loader.
|
||||||
|
*
|
||||||
|
* This instrumentation forces all class loaders to delegate to the bootstrap class loader
|
||||||
|
* for the classes that we have put in the bootstrap class loader.
|
||||||
|
*/
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public final class ClassloadingInstrumentation extends Instrumenter.Default {
|
||||||
|
public ClassloadingInstrumentation() {
|
||||||
|
super("classloading");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
// just an optimization to exclude common class loaders that are known to delegate to the
|
||||||
|
// bootstrap loader (or happen to _be_ the bootstrap loader)
|
||||||
|
return not(named("java.lang.ClassLoader"))
|
||||||
|
.and(not(named("com.ibm.oti.vm.BootstrapClassLoader")))
|
||||||
|
.and(not(named("datadog.trace.bootstrap.AgentClassLoader")))
|
||||||
|
.and(extendsClass(named("java.lang.ClassLoader")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {Constants.class.getName()};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod()
|
||||||
|
.and(named("loadClass"))
|
||||||
|
.and(
|
||||||
|
takesArguments(1)
|
||||||
|
.and(takesArgument(0, named("java.lang.String")))
|
||||||
|
.or(
|
||||||
|
takesArguments(2)
|
||||||
|
.and(takesArgument(0, named("java.lang.String")))
|
||||||
|
.and(takesArgument(1, named("boolean")))))
|
||||||
|
.and(isPublic().or(isProtected()))
|
||||||
|
.and(not(isStatic())),
|
||||||
|
ClassloadingInstrumentation.class.getName() + "$LoadClassAdvice");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class LoadClassAdvice {
|
||||||
|
@Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
|
||||||
|
public static Class<?> onEnter(
|
||||||
|
@Advice.Argument(0) final String name, @Advice.Local("_callDepth") int callDepth) {
|
||||||
|
callDepth = CallDepthThreadLocalMap.incrementCallDepth(ClassLoader.class);
|
||||||
|
if (callDepth > 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (final String prefix : Constants.BOOTSTRAP_PACKAGE_PREFIXES) {
|
||||||
|
if (name.startsWith(prefix)) {
|
||||||
|
try {
|
||||||
|
return Class.forName(name, false, null);
|
||||||
|
} catch (final ClassNotFoundException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class)
|
||||||
|
public static void onExit(
|
||||||
|
@Advice.Local("_callDepth") final int callDepth,
|
||||||
|
@Advice.Return(readOnly = false) Class<?> result,
|
||||||
|
@Advice.Enter final Class<?> resultFromBootstrapLoader) {
|
||||||
|
if (callDepth > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CallDepthThreadLocalMap.reset(ClassLoader.class);
|
||||||
|
if (resultFromBootstrapLoader != null) {
|
||||||
|
result = resultFromBootstrapLoader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import datadog.trace.agent.test.AgentTestRunner
|
||||||
|
|
||||||
|
class ClassloadingTest extends AgentTestRunner {
|
||||||
|
def "delegates to bootstrap class loader for agent classes"() {
|
||||||
|
setup:
|
||||||
|
def classLoader = new NonDelegatingURLClassLoader()
|
||||||
|
|
||||||
|
when:
|
||||||
|
Class<?> clazz
|
||||||
|
try {
|
||||||
|
clazz = Class.forName("datadog.trace.api.GlobalTracer", false, classLoader)
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
then:
|
||||||
|
assert clazz != null
|
||||||
|
assert clazz.getClassLoader() == null
|
||||||
|
}
|
||||||
|
|
||||||
|
static class NonDelegatingURLClassLoader extends URLClassLoader {
|
||||||
|
|
||||||
|
NonDelegatingURLClassLoader() {
|
||||||
|
super(new URL[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
synchronized (getClassLoadingLock(name)) {
|
||||||
|
Class<?> clazz = findLoadedClass(name)
|
||||||
|
if (clazz == null) {
|
||||||
|
clazz = findClass(name)
|
||||||
|
}
|
||||||
|
if (resolve) {
|
||||||
|
resolveClass(clazz)
|
||||||
|
}
|
||||||
|
return clazz
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,8 @@ testSets {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
testCompile project(':dd-java-agent:instrumentation:classloading')
|
||||||
|
|
||||||
//This seems to be the earliest version that has org.apache.catalina.loader.WebappClassLoaderBase
|
//This seems to be the earliest version that has org.apache.catalina.loader.WebappClassLoaderBase
|
||||||
//Older versions would require slightly different instrumentation.
|
//Older versions would require slightly different instrumentation.
|
||||||
testCompile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '8.0.14'
|
testCompile group: 'org.apache.tomcat', name: 'tomcat-catalina', version: '8.0.14'
|
|
@ -1,11 +1,9 @@
|
||||||
package datadog.trace.instrumentation.couchbase.client;
|
package datadog.trace.instrumentation.couchbase.client;
|
||||||
|
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
|
||||||
import com.couchbase.client.java.CouchbaseCluster;
|
import com.couchbase.client.java.CouchbaseCluster;
|
||||||
|
@ -29,10 +27,8 @@ public class CouchbaseBucketInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface())
|
return named("com.couchbase.client.java.bucket.DefaultAsyncBucketManager")
|
||||||
.and(
|
.or(named("com.couchbase.client.java.CouchbaseAsyncBucket"));
|
||||||
named("com.couchbase.client.java.bucket.DefaultAsyncBucketManager")
|
|
||||||
.or(named("com.couchbase.client.java.CouchbaseAsyncBucket")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +60,7 @@ public class CouchbaseBucketInstrumentation extends Instrumenter.Default {
|
||||||
return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class);
|
return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit(onThrowable = Throwable.class)
|
||||||
public static void subscribeResult(
|
public static void subscribeResult(
|
||||||
@Advice.Enter final int callDepth,
|
@Advice.Enter final int callDepth,
|
||||||
@Advice.Origin final Method method,
|
@Advice.Origin final Method method,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package datadog.trace.instrumentation.couchbase.client;
|
package datadog.trace.instrumentation.couchbase.client;
|
||||||
|
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
@ -29,10 +28,8 @@ public class CouchbaseClusterInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface())
|
return named("com.couchbase.client.java.cluster.DefaultAsyncClusterManager")
|
||||||
.and(
|
.or(named("com.couchbase.client.java.CouchbaseAsyncCluster"));
|
||||||
named("com.couchbase.client.java.cluster.DefaultAsyncClusterManager")
|
|
||||||
.or(named("com.couchbase.client.java.CouchbaseAsyncCluster")));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -64,7 +61,7 @@ public class CouchbaseClusterInstrumentation extends Instrumenter.Default {
|
||||||
return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class);
|
return CallDepthThreadLocalMap.incrementCallDepth(CouchbaseCluster.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Advice.OnMethodExit
|
@Advice.OnMethodExit(onThrowable = Throwable.class)
|
||||||
public static void subscribeResult(
|
public static void subscribeResult(
|
||||||
@Advice.Enter final int callDepth,
|
@Advice.Enter final int callDepth,
|
||||||
@Advice.Origin final Method method,
|
@Advice.Origin final Method method,
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
package datadog.trace.instrumentation.couchbase.client;
|
package datadog.trace.instrumentation.couchbase.client;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
@ -31,7 +32,9 @@ public class CouchbaseNetworkInstrumentation extends Instrumenter.Default {
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
// Exact class because private fields are used
|
// Exact class because private fields are used
|
||||||
return safeHasSuperType(named("com.couchbase.client.core.endpoint.AbstractGenericHandler"));
|
return nameStartsWith("com.couchbase.client.")
|
||||||
|
.<TypeDescription>and(
|
||||||
|
extendsClass(named("com.couchbase.client.core.endpoint.AbstractGenericHandler")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
package datadog.trace.instrumentation.dropwizard.view;
|
package datadog.trace.instrumentation.dropwizard.view;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -34,7 +32,7 @@ public final class DropwizardViewInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("io.dropwizard.views.ViewRenderer")));
|
return implementsInterface(named("io.dropwizard.views.ViewRenderer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -113,7 +113,6 @@ class DropwizardTest extends HttpServerTest<DropwizardTestSupport, Servlet3Decor
|
||||||
tags {
|
tags {
|
||||||
"$Tags.COMPONENT" serverDecorator.component()
|
"$Tags.COMPONENT" serverDecorator.component()
|
||||||
"$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER
|
"$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER
|
||||||
"$Tags.PEER_HOSTNAME" { it == "localhost" || it == "127.0.0.1" }
|
|
||||||
"$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional
|
"$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional
|
||||||
"$Tags.PEER_PORT" Integer
|
"$Tags.PEER_PORT" Integer
|
||||||
"$Tags.HTTP_URL" "${endpoint.resolve(address)}"
|
"$Tags.HTTP_URL" "${endpoint.resolve(address)}"
|
||||||
|
|
|
@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE;
|
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
@ -42,7 +40,7 @@ public class Elasticsearch5RestClientInstrumentation extends Instrumenter.Defaul
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(named("org.elasticsearch.client.RestClient"));
|
return named("org.elasticsearch.client.RestClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE;
|
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchRestClientDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
@ -43,7 +41,7 @@ public class Elasticsearch6RestClientInstrumentation extends Instrumenter.Defaul
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(named("org.elasticsearch.client.RestClient"));
|
return named("org.elasticsearch.client.RestClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -35,7 +33,7 @@ public class Elasticsearch2TransportClientInstrumentation extends Instrumenter.D
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
// If we want to be more generic, we could instrument the interface instead:
|
// If we want to be more generic, we could instrument the interface instead:
|
||||||
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
||||||
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient"));
|
return named("org.elasticsearch.client.support.AbstractClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class TransportActionListener<T extends ActionResponse> implements Action
|
||||||
|
|
||||||
if (response instanceof BaseNodesResponse) {
|
if (response instanceof BaseNodesResponse) {
|
||||||
final BaseNodesResponse resp = (BaseNodesResponse) response;
|
final BaseNodesResponse resp = (BaseNodesResponse) response;
|
||||||
if (resp.failures().length > 0) {
|
if (resp.failures() != null && resp.failures().length > 0) {
|
||||||
span.setTag("elasticsearch.node.failures", resp.failures().length);
|
span.setTag("elasticsearch.node.failures", resp.failures().length);
|
||||||
}
|
}
|
||||||
span.setTag("elasticsearch.node.cluster.name", resp.getClusterName().value());
|
span.setTag("elasticsearch.node.cluster.name", resp.getClusterName().value());
|
||||||
|
|
|
@ -4,6 +4,7 @@ import datadog.trace.agent.test.utils.PortUtils
|
||||||
import datadog.trace.api.DDSpanTypes
|
import datadog.trace.api.DDSpanTypes
|
||||||
import datadog.trace.bootstrap.instrumentation.api.Tags
|
import datadog.trace.bootstrap.instrumentation.api.Tags
|
||||||
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
|
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest
|
||||||
|
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsRequest
|
||||||
import org.elasticsearch.client.transport.TransportClient
|
import org.elasticsearch.client.transport.TransportClient
|
||||||
import org.elasticsearch.common.io.FileSystemUtils
|
import org.elasticsearch.common.io.FileSystemUtils
|
||||||
import org.elasticsearch.common.settings.Settings
|
import org.elasticsearch.common.settings.Settings
|
||||||
|
@ -104,6 +105,41 @@ class Elasticsearch2TransportClientTest extends AgentTestRunner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def "test elasticsearch stats"() {
|
||||||
|
setup:
|
||||||
|
def result = client.admin().cluster().clusterStats(new ClusterStatsRequest(new String[0]))
|
||||||
|
|
||||||
|
def status = result.get().status
|
||||||
|
def failures = result.get().failures()
|
||||||
|
|
||||||
|
expect:
|
||||||
|
status.name() == "GREEN"
|
||||||
|
failures == null
|
||||||
|
|
||||||
|
assertTraces(1) {
|
||||||
|
trace(0, 1) {
|
||||||
|
span(0) {
|
||||||
|
serviceName "elasticsearch"
|
||||||
|
resourceName "ClusterStatsAction"
|
||||||
|
operationName "elasticsearch.query"
|
||||||
|
spanType DDSpanTypes.ELASTICSEARCH
|
||||||
|
tags {
|
||||||
|
"$Tags.COMPONENT" "elasticsearch-java"
|
||||||
|
"$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT
|
||||||
|
"$Tags.PEER_HOSTNAME" "127.0.0.1"
|
||||||
|
"$Tags.PEER_HOST_IPV4" "127.0.0.1"
|
||||||
|
"$Tags.PEER_PORT" tcpPort
|
||||||
|
"$Tags.DB_TYPE" "elasticsearch"
|
||||||
|
"elasticsearch.action" "ClusterStatsAction"
|
||||||
|
"elasticsearch.request" "ClusterStatsRequest"
|
||||||
|
"elasticsearch.node.cluster.name" "test-cluster"
|
||||||
|
defaultTags()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def "test elasticsearch error"() {
|
def "test elasticsearch error"() {
|
||||||
when:
|
when:
|
||||||
client.prepareGet(indexName, indexType, id).get()
|
client.prepareGet(indexName, indexType, id).get()
|
||||||
|
|
|
@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -36,7 +34,7 @@ public class Elasticsearch53TransportClientInstrumentation extends Instrumenter.
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
// If we want to be more generic, we could instrument the interface instead:
|
// If we want to be more generic, we could instrument the interface instead:
|
||||||
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
||||||
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient"));
|
return named("org.elasticsearch.client.support.AbstractClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -35,7 +33,7 @@ public class Elasticsearch5TransportClientInstrumentation extends Instrumenter.D
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
// If we want to be more generic, we could instrument the interface instead:
|
// If we want to be more generic, we could instrument the interface instead:
|
||||||
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
||||||
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient"));
|
return named("org.elasticsearch.client.support.AbstractClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -4,10 +4,8 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSp
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
import static datadog.trace.instrumentation.elasticsearch.ElasticsearchTransportClientDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -39,7 +37,7 @@ public class Elasticsearch6TransportClientInstrumentation extends Instrumenter.D
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
// If we want to be more generic, we could instrument the interface instead:
|
// If we want to be more generic, we could instrument the interface instead:
|
||||||
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
// .and(safeHasSuperType(named("org.elasticsearch.client.ElasticsearchClient"))))
|
||||||
return not(isInterface()).and(named("org.elasticsearch.client.support.AbstractClient"));
|
return named("org.elasticsearch.client.support.AbstractClient");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -24,11 +24,6 @@ public class FinatraDecorator extends HttpServerDecorator<Request, Request, Resp
|
||||||
return URI.create(request.uri());
|
return URI.create(request.uri());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String peerHostname(final Request request) {
|
|
||||||
return request.remoteHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String peerHostIP(final Request request) {
|
protected String peerHostIP(final Request request) {
|
||||||
return request.remoteAddress().getHostAddress();
|
return request.remoteAddress().getHostAddress();
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
package datadog.trace.instrumentation.finatra;
|
package datadog.trace.instrumentation.finatra;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.finatra.FinatraDecorator.DECORATE;
|
import static datadog.trace.instrumentation.finatra.FinatraDecorator.DECORATE;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
@ -51,8 +50,9 @@ public class FinatraInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||||
return not(isInterface())
|
return nameStartsWith("com.twitter.finatra.")
|
||||||
.and(safeHasSuperType(named("com.twitter.finatra.http.internal.routing.Route")));
|
.<TypeDescription>and(
|
||||||
|
extendsClass(named("com.twitter.finatra.http.internal.routing.Route")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -103,7 +103,6 @@ class GlassFishServerTest extends HttpServerTest<GlassFish, Servlet3Decorator> {
|
||||||
tags {
|
tags {
|
||||||
"$Tags.COMPONENT" serverDecorator.component()
|
"$Tags.COMPONENT" serverDecorator.component()
|
||||||
"$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER
|
"$Tags.SPAN_KIND" Tags.SPAN_KIND_SERVER
|
||||||
"$Tags.PEER_HOSTNAME" { it == "localhost" || it == "127.0.0.1" }
|
|
||||||
"$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional
|
"$Tags.PEER_HOST_IPV4" { it == null || it == "127.0.0.1" } // Optional
|
||||||
"$Tags.PEER_PORT" Integer
|
"$Tags.PEER_PORT" Integer
|
||||||
"$Tags.HTTP_STATUS" endpoint.status
|
"$Tags.HTTP_STATUS" endpoint.status
|
||||||
|
|
|
@ -26,11 +26,6 @@ public class GrizzlyDecorator extends HttpServerDecorator<Request, Request, Resp
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String peerHostname(final Request request) {
|
|
||||||
return request.getRemoteHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String peerHostIP(final Request request) {
|
protected String peerHostIP(final Request request) {
|
||||||
return request.getRemoteAddr();
|
return request.getRemoteAddr();
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
@ -31,7 +29,7 @@ public class CriteriaInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Criteria")));
|
return implementsInterface(named("org.hibernate.Criteria"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
@ -33,7 +31,7 @@ public class QueryInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Query")));
|
return implementsInterface(named("org.hibernate.Query"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
@ -40,7 +39,7 @@ public class SessionFactoryInstrumentation extends AbstractHibernateInstrumentat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SessionFactory")));
|
return implementsInterface(named("org.hibernate.SessionFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,7 +52,7 @@ public class SessionFactoryInstrumentation extends AbstractHibernateInstrumentat
|
||||||
returns(
|
returns(
|
||||||
named("org.hibernate.Session")
|
named("org.hibernate.Session")
|
||||||
.or(named("org.hibernate.StatelessSession"))
|
.or(named("org.hibernate.StatelessSession"))
|
||||||
.or(safeHasSuperType(named("org.hibernate.Session"))))),
|
.or(hasInterface(named("org.hibernate.Session"))))),
|
||||||
SessionFactoryInstrumentation.class.getName() + "$SessionFactoryAdvice");
|
SessionFactoryInstrumentation.class.getName() + "$SessionFactoryAdvice");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
@ -48,10 +47,8 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface())
|
return implementsInterface(
|
||||||
.and(
|
named("org.hibernate.Session").or(named("org.hibernate.StatelessSession")));
|
||||||
safeHasSuperType(
|
|
||||||
named("org.hibernate.Session").or(named("org.hibernate.StatelessSession"))));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,11 +96,11 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
SessionInstrumentation.class.getName() + "$GetTransactionAdvice");
|
SessionInstrumentation.class.getName() + "$GetTransactionAdvice");
|
||||||
|
|
||||||
transformers.put(
|
transformers.put(
|
||||||
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Query")))),
|
isMethod().and(returns(hasInterface(named("org.hibernate.Query")))),
|
||||||
SessionInstrumentation.class.getName() + "$GetQueryAdvice");
|
SessionInstrumentation.class.getName() + "$GetQueryAdvice");
|
||||||
|
|
||||||
transformers.put(
|
transformers.put(
|
||||||
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Criteria")))),
|
isMethod().and(returns(hasInterface(named("org.hibernate.Criteria")))),
|
||||||
SessionInstrumentation.class.getName() + "$GetCriteriaAdvice");
|
SessionInstrumentation.class.getName() + "$GetCriteriaAdvice");
|
||||||
|
|
||||||
return transformers;
|
return transformers;
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
package datadog.trace.instrumentation.hibernate.core.v3_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -31,7 +29,7 @@ public class TransactionInstrumentation extends AbstractHibernateInstrumentation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Transaction")));
|
return implementsInterface(named("org.hibernate.Transaction"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
@ -31,7 +29,7 @@ public class CriteriaInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Criteria")));
|
return implementsInterface(named("org.hibernate.Criteria"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
@ -33,7 +31,7 @@ public class QueryInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Query")));
|
return implementsInterface(named("org.hibernate.Query"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
|
@ -34,7 +32,7 @@ public class SessionFactoryInstrumentation extends AbstractHibernateInstrumentat
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SessionFactory")));
|
return implementsInterface(named("org.hibernate.SessionFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
|
||||||
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
import static datadog.trace.instrumentation.hibernate.HibernateDecorator.DECORATOR;
|
||||||
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
import static datadog.trace.instrumentation.hibernate.SessionMethodUtils.SCOPE_ONLY_METHODS;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
@ -46,7 +45,7 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SharedSessionContract")));
|
return implementsInterface(named("org.hibernate.SharedSessionContract"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -93,11 +92,11 @@ public class SessionInstrumentation extends AbstractHibernateInstrumentation {
|
||||||
SessionInstrumentation.class.getName() + "$GetTransactionAdvice");
|
SessionInstrumentation.class.getName() + "$GetTransactionAdvice");
|
||||||
|
|
||||||
transformers.put(
|
transformers.put(
|
||||||
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Query")))),
|
isMethod().and(returns(hasInterface(named("org.hibernate.Query")))),
|
||||||
SessionInstrumentation.class.getName() + "$GetQueryAdvice");
|
SessionInstrumentation.class.getName() + "$GetQueryAdvice");
|
||||||
|
|
||||||
transformers.put(
|
transformers.put(
|
||||||
isMethod().and(returns(safeHasSuperType(named("org.hibernate.Criteria")))),
|
isMethod().and(returns(hasInterface(named("org.hibernate.Criteria")))),
|
||||||
SessionInstrumentation.class.getName() + "$GetCriteriaAdvice");
|
SessionInstrumentation.class.getName() + "$GetCriteriaAdvice");
|
||||||
|
|
||||||
return transformers;
|
return transformers;
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
package datadog.trace.instrumentation.hibernate.core.v4_0;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -31,7 +29,7 @@ public class TransactionInstrumentation extends AbstractHibernateInstrumentation
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.Transaction")));
|
return implementsInterface(named("org.hibernate.Transaction"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v4_3;
|
package datadog.trace.instrumentation.hibernate.core.v4_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
@ -47,7 +45,7 @@ public class ProcedureCallInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.procedure.ProcedureCall")));
|
return implementsInterface(named("org.hibernate.procedure.ProcedureCall"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
package datadog.trace.instrumentation.hibernate.core.v4_3;
|
package datadog.trace.instrumentation.hibernate.core.v4_3;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.hasInterface;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.implementsInterface;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
|
@ -53,7 +52,7 @@ public class SessionInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return not(isInterface()).and(safeHasSuperType(named("org.hibernate.SharedSessionContract")));
|
return implementsInterface(named("org.hibernate.SharedSessionContract"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -61,7 +60,7 @@ public class SessionInstrumentation extends Instrumenter.Default {
|
||||||
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
|
||||||
|
|
||||||
transformers.put(
|
transformers.put(
|
||||||
isMethod().and(returns(safeHasSuperType(named("org.hibernate.procedure.ProcedureCall")))),
|
isMethod().and(returns(hasInterface(named("org.hibernate.procedure.ProcedureCall")))),
|
||||||
SessionInstrumentation.class.getName() + "$GetProcedureCallAdvice");
|
SessionInstrumentation.class.getName() + "$GetProcedureCallAdvice");
|
||||||
|
|
||||||
return transformers;
|
return transformers;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package datadog.trace.instrumentation.http_url_connection;
|
package datadog.trace.instrumentation.http_url_connection;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.propagate;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
|
@ -9,6 +9,7 @@ import static datadog.trace.instrumentation.http_url_connection.HttpUrlConnectio
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@ import net.bytebuddy.asm.Advice;
|
||||||
import net.bytebuddy.description.method.MethodDescription;
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
import net.bytebuddy.description.type.TypeDescription;
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
import net.bytebuddy.matcher.ElementMatcher;
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
import net.bytebuddy.matcher.ElementMatchers;
|
||||||
|
|
||||||
@AutoService(Instrumenter.class)
|
@AutoService(Instrumenter.class)
|
||||||
public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
|
public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
|
||||||
|
@ -35,9 +37,11 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return safeHasSuperType(named("java.net.HttpURLConnection"))
|
return nameStartsWith("java.net.")
|
||||||
|
.or(ElementMatchers.<TypeDescription>nameStartsWith("sun.net"))
|
||||||
// This class is a simple delegator. Skip because it does not update its `connected` field.
|
// This class is a simple delegator. Skip because it does not update its `connected` field.
|
||||||
.and(not(named("sun.net.www.protocol.https.HttpsURLConnectionImpl")));
|
.and(not(named("sun.net.www.protocol.https.HttpsURLConnectionImpl")))
|
||||||
|
.and(extendsClass(named("java.net.HttpURLConnection")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -3,7 +3,10 @@ package datadog.trace.instrumentation.http_url_connection;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan;
|
||||||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.*;
|
import static net.bytebuddy.matcher.ElementMatchers.is;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
|
||||||
import com.google.auto.service.AutoService;
|
import com.google.auto.service.AutoService;
|
||||||
import datadog.trace.agent.tooling.Instrumenter;
|
import datadog.trace.agent.tooling.Instrumenter;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package datadog.trace.instrumentation.hystrix;
|
package datadog.trace.instrumentation.hystrix;
|
||||||
|
|
||||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
|
import static datadog.trace.agent.tooling.bytebuddy.matcher.DDElementMatchers.extendsClass;
|
||||||
import static datadog.trace.instrumentation.hystrix.HystrixDecorator.DECORATE;
|
import static datadog.trace.instrumentation.hystrix.HystrixDecorator.DECORATE;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
@ -29,7 +29,7 @@ public class HystrixInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
return safeHasSuperType(
|
return extendsClass(
|
||||||
named("com.netflix.hystrix.HystrixCommand")
|
named("com.netflix.hystrix.HystrixCommand")
|
||||||
.or(named("com.netflix.hystrix.HystrixObservableCommand")));
|
.or(named("com.netflix.hystrix.HystrixObservableCommand")));
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue