-
Notifications
You must be signed in to change notification settings - Fork 1k
Exclude jdbc wrappers from instrumentation #14760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if this deserves a dedicated test or not
.../javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jdbc/JdbcSingletons.java
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that a test would be nice here, but could be in a follow-up effort.
return DATASOURCE_INSTRUMENTER; | ||
} | ||
|
||
private static final Cache<Class<?>, Boolean> wrapperClassCache = Cache.weak(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does ClassValue work here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't work in the standard way because we need the actual object to decide whether it can be unwrapped. I think the most straightforward way we could use class value would be to use
private static final ClassValue<IsWrapper> wrapperClassCache =
new ClassValue<IsWrapper>() {
@Override
protected IsWrapper computeValue(Class<?> type) {
return new IsWrapper();
}
};
private static class IsWrapper {
volatile boolean valueSet;
volatile boolean value;
boolean getValue(Wrapper object, Class<? extends Wrapper> clazz) {
if (valueSet) {
return value;
}
value = isWrapperInternal(object, clazz);
valueSet = true;
return true;
}
}
/**
* Returns true if the given object is a wrapper and shouldn't be instrumented. We'll instrument
* the underlying object called by the wrapper instead.
*/
public static <T extends Wrapper> boolean isWrapper(T object, Class<T> clazz) {
IsWrapper isWrapper = wrapperClassCache.get(clazz);
return isWrapper.getValue(object, clazz);
}
private static boolean isWrapperInternal(Wrapper object, Class<? extends Wrapper> clazz) {
try {
// we are dealing with a wrapper when the object unwraps to a different instance
if (object.isWrapperFor(clazz)) {
Wrapper unwrapped = object.unwrap(clazz);
if (object != unwrapped) {
return true;
}
}
} catch (SQLException | AbstractMethodError e) {
// ignore
}
return false;
}
or
private static final int UNDEFINED = 0;
private static final int WRAPPER = 1;
private static final int NOT_WRAPPER = -1;
private static final ClassValue<AtomicInteger> wrapperClassCache =
new ClassValue<AtomicInteger>() {
@Override
protected AtomicInteger computeValue(Class<?> type) {
return new AtomicInteger();
}
};
/**
* Returns true if the given object is a wrapper and shouldn't be instrumented. We'll instrument
* the underlying object called by the wrapper instead.
*/
public static <T extends Wrapper> boolean isWrapper(T object, Class<T> clazz) {
AtomicInteger isWrapper = wrapperClassCache.get(clazz);
int value = isWrapper.get();
if (value != UNDEFINED) {
return value == WRAPPER;
}
boolean result = isWrapperInternal(object, clazz);
isWrapper.set(result ? WRAPPER : NOT_WRAPPER);
return result;
}
private static <T extends Wrapper> boolean isWrapperInternal(T object, Class<T> clazz) {
try {
// we are dealing with a wrapper when the object unwraps to a different instance
if (object.isWrapperFor(clazz)) {
Wrapper unwrapped = object.unwrap(clazz);
if (object != unwrapped) {
return true;
}
}
} catch (SQLException | AbstractMethodError e) {
// ignore
}
return false;
}
which is slightly more code than the weak cache, but not too bad really.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't work in the standard way because we need the actual object to decide whether it can be unwrapped
ah, makes sense
I like the IsWrapper
version better than the AtomicInteger
version, but I also like your existing implementation given that it's not really a standard usage of ClassValue anyways
Resolves #14733
Currently we start tracing with the the first jdbc class that is called. This PR moves the tracing to the first non-wrapper. Wrapper is detected by attempting to unwrap the input object, if it unwraps to a different instance. Note that shardingspere, that was used in the issue, types can't be unwrapped but it is fine because these types are already excluded via a ignored types configurer. What affects the problem described in the issue is skipping the hikari cp wrapper that is on top of the shardingsphere.