SafeHasSuperTypeMatcher: handle exceptions when getting class erasure
This commit is contained in:
parent
a7c63b9261
commit
36ec5d51e5
|
@ -1,7 +1,5 @@
|
|||
package datadog.trace.agent.tooling;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.erasure;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -32,7 +30,7 @@ public class ByteBuddyElementMatchers {
|
|||
*/
|
||||
public static <T extends TypeDescription> ElementMatcher.Junction<T> safeHasSuperType(
|
||||
final ElementMatcher<? super TypeDescription> matcher) {
|
||||
return safeHasGenericSuperType(erasure(matcher));
|
||||
return safeHasGenericSuperType(new SafeErasureMatcher(matcher));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,6 +48,29 @@ public class ByteBuddyElementMatchers {
|
|||
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 target) {
|
||||
try {
|
||||
return target.asErasure();
|
||||
} catch (final Exception e) {
|
||||
log.debug("Exception trying to get interfaces:", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An element matcher that matches a super type. This is different from {@link
|
||||
* net.bytebuddy.matcher.HasSuperTypeMatcher} in the following way:
|
||||
|
@ -119,10 +140,13 @@ public class ByteBuddyElementMatchers {
|
|||
private boolean hasInterface(
|
||||
final TypeDefinition typeDefinition, final Set<TypeDescription> checkedInterfaces) {
|
||||
for (final TypeDefinition interfaceType : safeGetInterfaces(typeDefinition)) {
|
||||
if (checkedInterfaces.add(interfaceType.asErasure())
|
||||
&& (matcher.matches(interfaceType.asGenericType())
|
||||
|| hasInterface(interfaceType, checkedInterfaces))) {
|
||||
return true;
|
||||
final TypeDescription erasure = safeAsErasure(interfaceType);
|
||||
if (erasure != null) {
|
||||
if (checkedInterfaces.add(interfaceType.asErasure())
|
||||
&& (matcher.matches(interfaceType.asGenericType())
|
||||
|| hasInterface(interfaceType, checkedInterfaces))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -153,4 +177,98 @@ public class ByteBuddyElementMatchers {
|
|||
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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package datadog.trace.agent.tooling;
|
||||
|
||||
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.failSafe;
|
||||
import static datadog.trace.agent.tooling.Utils.getConfigEnabled;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.any;
|
||||
|
||||
|
@ -80,7 +81,14 @@ public interface Instrumenter {
|
|||
|
||||
AgentBuilder.Identified.Extendable agentBuilder =
|
||||
parentAgentBuilder
|
||||
.type(safeTypeMatcher(typeMatcher()), classLoaderMatcher())
|
||||
.type(
|
||||
failSafe(
|
||||
typeMatcher(),
|
||||
"Instrumentation type matcher unexpected exception: " + getClass().getName()),
|
||||
failSafe(
|
||||
classLoaderMatcher(),
|
||||
"Instrumentation class loader matcher unexpected exception: "
|
||||
+ getClass().getName()))
|
||||
.and(new MuzzleMatcher())
|
||||
.transform(DDTransformers.defaultTransformers());
|
||||
agentBuilder = injectHelperClasses(agentBuilder);
|
||||
|
@ -88,23 +96,6 @@ public interface Instrumenter {
|
|||
return agentBuilder.asDecorator();
|
||||
}
|
||||
|
||||
/** Wrap an ElementMatcher in a try-catch exception and log any exceptions. */
|
||||
private ElementMatcher<? super TypeDescription> safeTypeMatcher(
|
||||
final ElementMatcher<? super TypeDescription> instrumentationMatcher) {
|
||||
return new ElementMatcher<TypeDescription>() {
|
||||
@Override
|
||||
public boolean matches(TypeDescription target) {
|
||||
try {
|
||||
return instrumentationMatcher.matches(target);
|
||||
} catch (Exception e) {
|
||||
log.debug(
|
||||
"Instrumentation matcher unexpected exception: " + instrumentationPrimaryName, e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private AgentBuilder.Identified.Extendable injectHelperClasses(
|
||||
AgentBuilder.Identified.Extendable agentBuilder) {
|
||||
final String[] helperClassNames = helperClassNames();
|
||||
|
@ -151,7 +142,7 @@ public interface Instrumenter {
|
|||
instrumentationPrimaryName,
|
||||
getClass().getName(),
|
||||
classLoader);
|
||||
for (Reference.Mismatch mismatch : mismatches) {
|
||||
for (final Reference.Mismatch mismatch : mismatches) {
|
||||
log.debug("-- {}", mismatch);
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +150,7 @@ public interface Instrumenter {
|
|||
log.debug(
|
||||
"Applying instrumentation: {} -- {} on {}",
|
||||
instrumentationPrimaryName,
|
||||
this.getClass().getName(),
|
||||
getClass().getName(),
|
||||
classLoader);
|
||||
}
|
||||
return mismatches.size() == 0;
|
||||
|
|
Loading…
Reference in New Issue