-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,15 @@ | |
import io.opentelemetry.instrumentation.api.incubator.semconv.net.PeerServiceAttributesExtractor; | ||
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; | ||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; | ||
import io.opentelemetry.instrumentation.api.internal.cache.Cache; | ||
import io.opentelemetry.instrumentation.jdbc.internal.DbRequest; | ||
import io.opentelemetry.instrumentation.jdbc.internal.JdbcInstrumenterFactory; | ||
import io.opentelemetry.instrumentation.jdbc.internal.JdbcNetworkAttributesGetter; | ||
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; | ||
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; | ||
import io.opentelemetry.javaagent.bootstrap.jdbc.DbInfo; | ||
import java.sql.SQLException; | ||
import java.sql.Wrapper; | ||
import java.util.Collections; | ||
import javax.sql.DataSource; | ||
|
||
|
@@ -68,5 +71,31 @@ public static Instrumenter<DataSource, DbInfo> dataSourceInstrumenter() { | |
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 commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more.
ah, makes sense I like the |
||
|
||
/** | ||
* 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) { | ||
return wrapperClassCache.computeIfAbsent( | ||
object.getClass(), key -> isWrapperInternal(object, clazz)); | ||
} | ||
|
||
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)) { | ||
T unwrapped = object.unwrap(clazz); | ||
if (object != unwrapped) { | ||
return true; | ||
} | ||
} | ||
} catch (SQLException | AbstractMethodError e) { | ||
// ignore | ||
} | ||
return false; | ||
} | ||
|
||
private JdbcSingletons() {} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.