|
| 1 | +/* |
| 2 | + * Copyright The OpenTelemetry Authors |
| 3 | + * SPDX-License-Identifier: Apache-2.0 |
| 4 | + */ |
| 5 | + |
1 | 6 | package io.opentelemetry.instrumentation.spring.autoconfigure.internal.instrumentation.jdbc; |
2 | 7 |
|
3 | 8 | import com.google.errorprone.annotations.CanIgnoreReturnValue; |
4 | 9 | import io.opentelemetry.api.OpenTelemetry; |
5 | 10 | import io.opentelemetry.instrumentation.jdbc.datasource.JdbcTelemetry; |
6 | 11 | import io.opentelemetry.instrumentation.spring.autoconfigure.internal.properties.InstrumentationConfigUtil; |
7 | 12 | import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties; |
8 | | - |
9 | 13 | import javax.annotation.Nonnull; |
10 | 14 | import javax.annotation.Nullable; |
11 | 15 | import javax.sql.DataSource; |
12 | | - |
13 | 16 | import org.aopalliance.intercept.MethodInterceptor; |
14 | 17 | import org.aopalliance.intercept.MethodInvocation; |
15 | 18 | import org.springframework.aop.framework.ProxyFactory; |
@@ -46,65 +49,67 @@ private static boolean isRoutingDatasource(Object bean) { |
46 | 49 | } |
47 | 50 |
|
48 | 51 | @CanIgnoreReturnValue |
49 | | -@Override |
50 | | -public Object postProcessAfterInitialization(Object bean, String beanName) { |
51 | | - // Exclude scoped proxy beans to avoid double wrapping |
52 | | - if (bean instanceof DataSource |
53 | | - && !isRoutingDatasource(bean) |
54 | | - && !ScopedProxyUtils.isScopedTarget(beanName)) { |
55 | | - DataSource dataSource = (DataSource) bean; |
| 52 | + @Override |
| 53 | + public Object postProcessAfterInitialization(Object bean, String beanName) { |
| 54 | + // Exclude scoped proxy beans to avoid double wrapping |
| 55 | + if (bean instanceof DataSource |
| 56 | + && !isRoutingDatasource(bean) |
| 57 | + && !ScopedProxyUtils.isScopedTarget(beanName)) { |
| 58 | + DataSource dataSource = (DataSource) bean; |
56 | 59 |
|
57 | | - // Wrap the original DataSource with OpenTelemetry instrumentation |
58 | | - DataSource wrapped = JdbcTelemetry.builder(openTelemetryProvider.getObject()) |
59 | | - .setStatementSanitizationEnabled( |
60 | | - InstrumentationConfigUtil.isStatementSanitizationEnabled( |
61 | | - configPropertiesProvider.getObject(), |
62 | | - "otel.instrumentation.jdbc.statement-sanitizer.enabled")) |
63 | | - .setCaptureQueryParameters( |
64 | | - configPropertiesProvider |
65 | | - .getObject() |
66 | | - .getBoolean( |
67 | | - "otel.instrumentation.jdbc.experimental.capture-query-parameters", false)) |
68 | | - .setTransactionInstrumenterEnabled( |
69 | | - configPropertiesProvider |
70 | | - .getObject() |
71 | | - .getBoolean("otel.instrumentation.jdbc.experimental.transaction.enabled", false)) |
72 | | - .build() |
73 | | - .wrap(dataSource); |
| 60 | + // Wrap the original DataSource with OpenTelemetry instrumentation |
| 61 | + DataSource wrapped = |
| 62 | + JdbcTelemetry.builder(openTelemetryProvider.getObject()) |
| 63 | + .setStatementSanitizationEnabled( |
| 64 | + InstrumentationConfigUtil.isStatementSanitizationEnabled( |
| 65 | + configPropertiesProvider.getObject(), |
| 66 | + "otel.instrumentation.jdbc.statement-sanitizer.enabled")) |
| 67 | + .setCaptureQueryParameters( |
| 68 | + configPropertiesProvider |
| 69 | + .getObject() |
| 70 | + .getBoolean( |
| 71 | + "otel.instrumentation.jdbc.experimental.capture-query-parameters", false)) |
| 72 | + .setTransactionInstrumenterEnabled( |
| 73 | + configPropertiesProvider |
| 74 | + .getObject() |
| 75 | + .getBoolean( |
| 76 | + "otel.instrumentation.jdbc.experimental.transaction.enabled", false)) |
| 77 | + .build() |
| 78 | + .wrap(dataSource); |
74 | 79 |
|
75 | | - /** |
76 | | - * Spring Boot's configuration binding and rebinding mechanisms (such as those triggered by |
77 | | - * Nacos configuration refresh) may attempt to reconstruct beans using their concrete class |
78 | | - * constructors. If a custom DataSource implementation (such as OpenTelemetryDataSource) |
79 | | - * is returned directly, Spring may not find a suitable constructor during rebinding, resulting |
80 | | - * in errors like "ExistingValue must be an instance of com.zaxxer.hikari.HikariDataSource". |
81 | | - * |
82 | | - * To prevent this, we create a JDK dynamic proxy implementing only the DataSource interface. |
83 | | - * The proxy delegates all method calls to the wrapped (instrumented) DataSource. |
84 | | - * This approach "hides" the actual implementation class and ensures that Spring interacts only |
85 | | - * with the DataSource interface, avoiding issues related to constructor resolution or type casting |
86 | | - * during bean rebinding. |
87 | | - */ |
88 | | - ProxyFactory proxyFactory = new ProxyFactory(DataSource.class); |
89 | | - // Set the original bean as the target (important for AOP and bean lifecycle) |
90 | | - proxyFactory.setTarget(bean); |
91 | | - // Delegate all method calls to the wrapped, instrumented DataSource |
92 | | - proxyFactory.addAdvice( |
93 | | - new MethodInterceptor() { |
94 | | - @Nullable |
95 | | - @Override |
96 | | - public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { |
97 | | - return AopUtils.invokeJoinpointUsingReflection( |
98 | | - wrapped, invocation.getMethod(), invocation.getArguments()); |
99 | | - } |
100 | | - }); |
| 80 | + /** |
| 81 | + * Spring Boot's configuration binding and rebinding mechanisms (such as those triggered by |
| 82 | + * Nacos configuration refresh) may attempt to reconstruct beans using their concrete class |
| 83 | + * constructors. If a custom DataSource implementation (such as OpenTelemetryDataSource) is |
| 84 | + * returned directly, Spring may not find a suitable constructor during rebinding, resulting |
| 85 | + * in errors like "ExistingValue must be an instance of com.zaxxer.hikari.HikariDataSource". |
| 86 | + * |
| 87 | + * <p>To prevent this, we create a JDK dynamic proxy implementing only the DataSource |
| 88 | + * interface. The proxy delegates all method calls to the wrapped (instrumented) DataSource. |
| 89 | + * This approach "hides" the actual implementation class and ensures that Spring interacts |
| 90 | + * only with the DataSource interface, avoiding issues related to constructor resolution or |
| 91 | + * type casting during bean rebinding. |
| 92 | + */ |
| 93 | + ProxyFactory proxyFactory = new ProxyFactory(DataSource.class); |
| 94 | + // Set the original bean as the target (important for AOP and bean lifecycle) |
| 95 | + proxyFactory.setTarget(bean); |
| 96 | + // Delegate all method calls to the wrapped, instrumented DataSource |
| 97 | + proxyFactory.addAdvice( |
| 98 | + new MethodInterceptor() { |
| 99 | + @Nullable |
| 100 | + @Override |
| 101 | + public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { |
| 102 | + return AopUtils.invokeJoinpointUsingReflection( |
| 103 | + wrapped, invocation.getMethod(), invocation.getArguments()); |
| 104 | + } |
| 105 | + }); |
101 | 106 |
|
102 | | - // Return the proxy instead of the instrumented DataSource instance |
103 | | - // This ensures proper interaction with Spring's bean lifecycle and rebinding |
104 | | - return proxyFactory.getProxy(); |
| 107 | + // Return the proxy instead of the instrumented DataSource instance |
| 108 | + // This ensures proper interaction with Spring's bean lifecycle and rebinding |
| 109 | + return proxyFactory.getProxy(); |
| 110 | + } |
| 111 | + return bean; |
105 | 112 | } |
106 | | - return bean; |
107 | | -} |
108 | 113 |
|
109 | 114 | // To be one of the first bean post-processors to be executed |
110 | 115 | @Override |
|
0 commit comments