1010import io .opentelemetry .instrumentation .jdbc .datasource .JdbcTelemetry ;
1111import io .opentelemetry .instrumentation .spring .autoconfigure .internal .properties .InstrumentationConfigUtil ;
1212import io .opentelemetry .sdk .autoconfigure .spi .ConfigProperties ;
13+ import java .io .PrintWriter ;
14+ import java .sql .Connection ;
15+ import java .sql .SQLException ;
16+ import java .sql .SQLFeatureNotSupportedException ;
17+ import java .util .logging .Logger ;
1318import javax .sql .DataSource ;
19+ import org .springframework .aop .SpringProxy ;
20+ import org .springframework .aop .framework .AdvisedSupport ;
1421import org .springframework .aop .scope .ScopedProxyUtils ;
1522import org .springframework .beans .factory .ObjectProvider ;
1623import org .springframework .beans .factory .config .BeanPostProcessor ;
@@ -50,22 +57,28 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
5057 && !isRoutingDatasource (bean )
5158 && !ScopedProxyUtils .isScopedTarget (beanName )) {
5259 DataSource dataSource = (DataSource ) bean ;
53- return JdbcTelemetry .builder (openTelemetryProvider .getObject ())
54- .setStatementSanitizationEnabled (
55- InstrumentationConfigUtil .isStatementSanitizationEnabled (
56- configPropertiesProvider .getObject (),
57- "otel.instrumentation.jdbc.statement-sanitizer.enabled" ))
58- .setCaptureQueryParameters (
59- configPropertiesProvider
60- .getObject ()
61- .getBoolean (
62- "otel.instrumentation.jdbc.experimental.capture-query-parameters" , false ))
63- .setTransactionInstrumenterEnabled (
64- configPropertiesProvider
65- .getObject ()
66- .getBoolean ("otel.instrumentation.jdbc.experimental.transaction.enabled" , false ))
67- .build ()
68- .wrap (dataSource );
60+ DataSource otelDataSource =
61+ JdbcTelemetry .builder (openTelemetryProvider .getObject ())
62+ .setStatementSanitizationEnabled (
63+ InstrumentationConfigUtil .isStatementSanitizationEnabled (
64+ configPropertiesProvider .getObject (),
65+ "otel.instrumentation.jdbc.statement-sanitizer.enabled" ))
66+ .setCaptureQueryParameters (
67+ configPropertiesProvider
68+ .getObject ()
69+ .getBoolean (
70+ "otel.instrumentation.jdbc.experimental.capture-query-parameters" , false ))
71+ .setTransactionInstrumenterEnabled (
72+ configPropertiesProvider
73+ .getObject ()
74+ .getBoolean (
75+ "otel.instrumentation.jdbc.experimental.transaction.enabled" , false ))
76+ .build ()
77+ .wrap (dataSource );
78+
79+ // wrap instrumented data source into a proxy that unwraps to the original data source
80+ // see https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/13512
81+ return new DataSource$$Wrapper (otelDataSource , dataSource );
6982 }
7083 return bean ;
7184 }
@@ -75,4 +88,65 @@ public Object postProcessAfterInitialization(Object bean, String beanName) {
7588 public int getOrder () {
7689 return Ordered .LOWEST_PRECEDENCE - 20 ;
7790 }
91+
92+ // Wrapper for DataSource that pretends to be a spring aop proxy. $$ in class name is commonly
93+ // used by bytecode proxies and is tested by
94+ // org.springframework.aop.support.AopUtils.isAopProxy(). This proxy can be unwrapped with
95+ // ((Advised) dataSource).getTargetSource().getTarget() and it unwraps to the original data
96+ // source.
97+ @ SuppressWarnings ("checkstyle:TypeName" )
98+ private static class DataSource$$Wrapper extends AdvisedSupport
99+ implements SpringProxy , DataSource {
100+ private final DataSource delegate ;
101+
102+ DataSource$$Wrapper (DataSource delegate , DataSource original ) {
103+ this .delegate = delegate ;
104+ setTarget (original );
105+ }
106+
107+ @ Override
108+ public Connection getConnection () throws SQLException {
109+ return delegate .getConnection ();
110+ }
111+
112+ @ Override
113+ public Connection getConnection (String username , String password ) throws SQLException {
114+ return delegate .getConnection (username , password );
115+ }
116+
117+ @ Override
118+ public PrintWriter getLogWriter () throws SQLException {
119+ return delegate .getLogWriter ();
120+ }
121+
122+ @ Override
123+ public void setLogWriter (PrintWriter out ) throws SQLException {
124+ delegate .setLogWriter (out );
125+ }
126+
127+ @ Override
128+ public void setLoginTimeout (int seconds ) throws SQLException {
129+ delegate .setLoginTimeout (seconds );
130+ }
131+
132+ @ Override
133+ public int getLoginTimeout () throws SQLException {
134+ return delegate .getLoginTimeout ();
135+ }
136+
137+ @ Override
138+ public Logger getParentLogger () throws SQLFeatureNotSupportedException {
139+ return delegate .getParentLogger ();
140+ }
141+
142+ @ Override
143+ public <T > T unwrap (Class <T > iface ) throws SQLException {
144+ return delegate .unwrap (iface );
145+ }
146+
147+ @ Override
148+ public boolean isWrapperFor (Class <?> iface ) throws SQLException {
149+ return delegate .isWrapperFor (iface );
150+ }
151+ }
78152}
0 commit comments