-
Notifications
You must be signed in to change notification settings - Fork 964
Use AOP proxy for DataSource telemetry to preserve bean type #13950
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
base: main
Are you sure you want to change the base?
Use AOP proxy for DataSource telemetry to preserve bean type #13950
Conversation
🔧 The result from spotlessApply was committed to the PR branch. |
@@ -30,10 +32,14 @@ public class JdbcInstrumentationAutoConfiguration { | |||
public JdbcInstrumentationAutoConfiguration() {} | |||
|
|||
@Bean | |||
// static to avoid "is not eligible for getting processed by all BeanPostProcessors" warning | |||
static DataSourcePostProcessor dataSourcePostProcessor( | |||
@Primary |
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 this work when application contains multiple data sources?
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.
The approach using DataSourcePostProcessor seems to pose challenges in resolving related issues due to the bean lifecycle, and it appears that automatic handling of multiple data sources may not be feasible with this method. Would there happen to be a better alternative?
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.
The approach using DataSourcePostProcessor seems to pose challenges in resolving related issues due to the bean lifecycle, and it appears that automatic handling of multiple data sources may not be feasible with this method. Would there happen to be a better alternative?
I'm sure there is. For example could try something like
diff --git a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/i
nstrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java
index 2435fbc2ba..58268f3523 100644
--- a/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java
+++ b/instrumentation/spring/spring-boot-autoconfigure/src/main/java/io/opentelemetry/instrumentation/spring/autoconfigure/internal/instrumentation/jdbc/DataSourcePostProcessor.java
@@ -10,8 +10,14 @@ import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.jdbc.datasource.JdbcTelemetry;
import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import javax.sql.DataSource;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.scope.ScopedProxyUtils;
+import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.core.Ordered;
@@ -50,7 +56,7 @@ final class DataSourcePostProcessor implements BeanPostProcessor, Ordered {
&& !isRoutingDatasource(bean)
&& !ScopedProxyUtils.isScopedTarget(beanName)) {
DataSource dataSource = (DataSource) bean;
- return JdbcTelemetry.builder(openTelemetryProvider.getObject())
+ DataSource wrapped = JdbcTelemetry.builder(openTelemetryProvider.getObject())
.setStatementSanitizationEnabled(
InstrumentationConfigUtil.isStatementSanitizationEnabled(
configPropertiesProvider.getObject(),
@@ -66,6 +72,21 @@ final class DataSourcePostProcessor implements BeanPostProcessor, Ordered {
.getBoolean("otel.instrumentation.jdbc.experimental.transaction.enabled", false))
.build()
.wrap(dataSource);
+
+ ProxyFactory proxyFactory = new ProxyFactory(DataSource.class);
+ proxyFactory.setTarget(bean);
+ proxyFactory.addAdvice(
+ new MethodInterceptor() {
+
+ @Nullable
+ @Override
+ public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
+ return AopUtils.invokeJoinpointUsingReflection(
+ wrapped, invocation.getMethod(), invocation.getArguments());
+ }
+ });
+
+ return proxyFactory.getProxy();
}
return bean;
}
I didn't test it with the original issue so can't be sure that it will help. The idea is that instead of returning our wrapped datasource we'll return a proxy that unwraps to the original datasource but delegates invocation to our wrapped datasource. This could work if whatever does the reconfiguration is smart enough to unwrap aop proxies, which it probably is. Note that there may be a better way to implement this.
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.
Should use new ProxyFactory(new Class<?>[] {DataSource.class});
instead of new ProxyFactory(DataSource.class);
@zeitlinger tagging you in case you wish to pick this up
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.
@jeanbisutti I think you worked on this bit before - do you want to pick that up?
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.
@laurit I’ve applied the changes using the method you suggested. Thank you!
a2ada34
to
8839778
Compare
8839778
to
7562bba
Compare
fbe953d
to
343f3be
Compare
🔧 The result from spotlessApply was committed to the PR branch. |
Perhaps it may be worth to try to reproduce #13512 by refreshing beans, to be sure that the change fixes the problem. It may have something specific to do in the case of an In the initial implementation, I have tried to avoid the usage of a proxy to ease the GraalVM native compatibility. It seems that the new implementation needs more GraalVM hints to make it work on the native mode:
@dngyukm If you could reproduce #13512 error, I could try to have a look. |
b7644a4
to
ce342d8
Compare
🔧 The result from spotlessApply was committed to the PR branch. |
I think we can replace the proxy with a custom class that looks like a proxy to spring but really is a plain java class. That class could implement the interfaces that are needed for unwrapping and be named so that spring believes that it could be a bytecode proxy. |
This PR changes the DataSource wrapping approach to fix #13512.