Skip to content

Commit 5ddcad3

Browse files
authored
Merge pull request #169 from DataDog/ark/c3p0
Instrument Connection Constructor to get connection info
2 parents 28bc1e7 + 4d5c06b commit 5ddcad3

File tree

6 files changed

+78
-103
lines changed

6 files changed

+78
-103
lines changed

dd-java-agent-ittests/src/test/groovy/com/datadoghq/agent/integration/jdbc/JDBCInstrumentationTest.groovy

Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,23 @@ package com.datadoghq.agent.integration.jdbc
22

33
import com.datadoghq.trace.DDTracer
44
import com.datadoghq.trace.writer.ListWriter
5-
import io.opentracing.util.GlobalTracer
5+
import dd.test.TestUtils
66
import org.apache.derby.jdbc.EmbeddedDriver
77
import org.h2.Driver
88
import org.hsqldb.jdbc.JDBCDriver
99
import spock.lang.Shared
1010
import spock.lang.Specification
1111
import spock.lang.Unroll
1212

13-
import java.lang.reflect.Field
1413
import java.sql.Connection
1514
import java.sql.PreparedStatement
1615
import java.sql.ResultSet
1716
import java.sql.Statement
1817

1918
class JDBCInstrumentationTest extends Specification {
2019

21-
ListWriter writer = new ListWriter()
22-
DDTracer tracer = new DDTracer(writer)
20+
final ListWriter writer = new ListWriter()
21+
final DDTracer tracer = new DDTracer(writer)
2322

2423
@Shared
2524
private Map<String, Connection> connections
@@ -43,16 +42,8 @@ class JDBCInstrumentationTest extends Specification {
4342
}
4443

4544
def setup() {
46-
try {
47-
GlobalTracer.register(tracer)
48-
} catch (final Exception e) {
49-
// Force it anyway using reflection
50-
final Field field = GlobalTracer.getDeclaredField("tracer")
51-
field.setAccessible(true)
52-
field.set(null, tracer)
53-
}
45+
TestUtils.registerOrReplaceGlobalTracer(tracer)
5446
writer.start()
55-
assert GlobalTracer.isRegistered()
5647
}
5748

5849
@Unroll
@@ -80,6 +71,7 @@ class JDBCInstrumentationTest extends Specification {
8071

8172
def tags = span.context().tags
8273
tags["db.type"] == driver
74+
tags["db.user"] == username
8375
tags["span.kind"] == "client"
8476
tags["component"] == "java-jdbc-statement"
8577

@@ -88,16 +80,16 @@ class JDBCInstrumentationTest extends Specification {
8880

8981
tags["thread.name"] != null
9082
tags["thread.id"] != null
91-
tags.size() == 7
83+
tags.size() == username == null ? 7 : 8
9284

9385
cleanup:
9486
statement.close()
9587

9688
where:
97-
driver | connection | query
98-
"h2" | connections.get("h2") | "SELECT 3"
99-
"derby" | connections.get("derby") | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
100-
"hsqldb" | connections.get("hsqldb") | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
89+
driver | connection | username | query
90+
"h2" | connections.get("h2") | null | "SELECT 3"
91+
"derby" | connections.get("derby") | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
92+
"hsqldb" | connections.get("hsqldb") | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
10193
}
10294

10395
@Unroll
@@ -126,6 +118,7 @@ class JDBCInstrumentationTest extends Specification {
126118

127119
def tags = span.context().tags
128120
tags["db.type"] == driver
121+
tags["db.user"] == username
129122
tags["span.kind"] == "client"
130123
tags["component"] == "java-jdbc-prepared_statement"
131124

@@ -134,16 +127,16 @@ class JDBCInstrumentationTest extends Specification {
134127

135128
tags["thread.name"] != null
136129
tags["thread.id"] != null
137-
tags.size() == 7
130+
tags.size() == username == null ? 7 : 8
138131

139132
cleanup:
140133
statement.close()
141134

142135
where:
143-
driver | connection | query
144-
"h2" | connections.get("h2") | "SELECT 3"
145-
"derby" | connections.get("derby") | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
146-
"hsqldb" | connections.get("hsqldb") | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
136+
driver | connection | username | query
137+
"h2" | connections.get("h2") | null | "SELECT 3"
138+
"derby" | connections.get("derby") | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
139+
"hsqldb" | connections.get("hsqldb") | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
147140
}
148141

149142
@Unroll
@@ -171,6 +164,7 @@ class JDBCInstrumentationTest extends Specification {
171164

172165
def tags = span.context().tags
173166
tags["db.type"] == driver
167+
tags["db.user"] == username
174168
tags["span.kind"] == "client"
175169
tags["component"] == "java-jdbc-prepared_statement"
176170

@@ -179,16 +173,16 @@ class JDBCInstrumentationTest extends Specification {
179173

180174
tags["thread.name"] != null
181175
tags["thread.id"] != null
182-
tags.size() == 7
176+
tags.size() == username == null ? 7 : 8
183177

184178
cleanup:
185179
statement.close()
186180

187181
where:
188-
driver | connection | query
189-
"h2" | connections.get("h2") | "SELECT 3"
190-
"derby" | connections.get("derby") | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
191-
"hsqldb" | connections.get("hsqldb") | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
182+
driver | connection | username | query
183+
"h2" | connections.get("h2") | null | "SELECT 3"
184+
"derby" | connections.get("derby") | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
185+
"hsqldb" | connections.get("hsqldb") | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
192186
}
193187

194188
@Unroll
@@ -217,6 +211,7 @@ class JDBCInstrumentationTest extends Specification {
217211

218212
def tags = span.context().tags
219213
tags["db.type"] == driver
214+
tags["db.user"] == username
220215
tags["span.kind"] == "client"
221216
tags["component"] == "java-jdbc-statement"
222217

@@ -225,16 +220,16 @@ class JDBCInstrumentationTest extends Specification {
225220

226221
tags["thread.name"] != null
227222
tags["thread.id"] != null
228-
tags.size() == 7
223+
tags.size() == username == null ? 7 : 8
229224

230225
cleanup:
231226
statement.close()
232227

233228
where:
234-
driver | connection | query
235-
"h2" | connections.get("h2") | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
236-
"derby" | connections.get("derby") | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))"
237-
"hsqldb" | connections.get("hsqldb") | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))"
229+
driver | connection | username | query
230+
"h2" | connections.get("h2") | null | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
231+
"derby" | connections.get("derby") | "APP" | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))"
232+
"hsqldb" | connections.get("hsqldb") | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))"
238233
}
239234

240235
@Unroll
@@ -261,6 +256,7 @@ class JDBCInstrumentationTest extends Specification {
261256

262257
def tags = span.context().tags
263258
tags["db.type"] == driver
259+
tags["db.user"] == username
264260
tags["span.kind"] == "client"
265261
tags["component"] == "java-jdbc-prepared_statement"
266262

@@ -269,15 +265,15 @@ class JDBCInstrumentationTest extends Specification {
269265

270266
tags["thread.name"] != null
271267
tags["thread.id"] != null
272-
tags.size() == 7
268+
tags.size() == username == null ? 7 : 8
273269

274270
cleanup:
275271
statement.close()
276272

277273
where:
278-
driver | connection | query
279-
"h2" | connections.get("h2") | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
274+
driver | connection | username | query
275+
"h2" | connections.get("h2") | null | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))"
280276
// Derby calls executeLargeUpdate from executeUpdate thus generating a nested span breaking this test.
281-
"hsqldb" | connections.get("hsqldb") | "CREATE TABLE PUBLIC.PS_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))"
277+
"hsqldb" | connections.get("hsqldb") | "SA" | "CREATE TABLE PUBLIC.PS_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))"
282278
}
283279
}

dd-java-agent/src/main/java/com/datadoghq/agent/TracingAgent.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static net.bytebuddy.matcher.ElementMatchers.any;
2222
import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader;
2323
import static net.bytebuddy.matcher.ElementMatchers.nameContains;
24+
import static net.bytebuddy.matcher.ElementMatchers.nameMatches;
2425
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
2526

2627
import dd.trace.Instrumenter;
@@ -77,6 +78,7 @@ public static void addByteBuddy(final Instrumentation inst) {
7778
.or(nameStartsWith("org.slf4j."))
7879
.or(nameContains("javassist"))
7980
.or(nameContains(".asm."))
81+
.or(nameMatches("com\\.mchange\\.v2\\.c3p0\\..*Proxy"))
8082
.ignore(
8183
any(),
8284
isBootstrapClassLoader()

dd-java-agent/src/main/java/com/datadoghq/agent/instrumentation/jdbc/ConnectionInstrumentation.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.datadoghq.agent.instrumentation.jdbc;
22

33
import static net.bytebuddy.matcher.ElementMatchers.hasSuperType;
4+
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
45
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
56
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
67
import static net.bytebuddy.matcher.ElementMatchers.named;
@@ -15,11 +16,13 @@
1516
import java.sql.PreparedStatement;
1617
import java.util.Map;
1718
import java.util.WeakHashMap;
19+
import lombok.Data;
1820
import net.bytebuddy.agent.builder.AgentBuilder;
1921
import net.bytebuddy.asm.Advice;
2022

2123
@AutoService(Instrumenter.class)
2224
public final class ConnectionInstrumentation implements Instrumenter {
25+
public static final Map<Connection, DBInfo> connectionInfo = new WeakHashMap<>();
2326
public static final Map<PreparedStatement, String> preparedStatements = new WeakHashMap<>();
2427

2528
@Override
@@ -32,15 +35,47 @@ public AgentBuilder instrument(final AgentBuilder agentBuilder) {
3235
nameStartsWith("prepare")
3336
.and(takesArgument(0, String.class))
3437
.and(returns(PreparedStatement.class)),
35-
ConnectionAdvice.class.getName()))
38+
ConnectionPrepareAdvice.class.getName()))
39+
.transform(
40+
DDAdvice.create().advice(isConstructor(), ConnectionConstructorAdvice.class.getName()))
3641
.asDecorator();
3742
}
3843

39-
public static class ConnectionAdvice {
44+
public static class ConnectionPrepareAdvice {
4045
@Advice.OnMethodExit(suppress = Throwable.class)
4146
public static void addDBInfo(
4247
@Advice.Argument(0) final String sql, @Advice.Return final PreparedStatement statement) {
4348
preparedStatements.put(statement, sql);
4449
}
4550
}
51+
52+
public static class ConnectionConstructorAdvice {
53+
@Advice.OnMethodExit(suppress = Throwable.class)
54+
public static void addDBInfo(@Advice.This final Connection connection) {
55+
try {
56+
final String url = connection.getMetaData().getURL();
57+
if (url != null) {
58+
// Remove end of url to prevent passwords from leaking:
59+
final String sanitizedURL = url.replaceAll("[?;].*", "");
60+
final String type = url.split(":")[1];
61+
String user = connection.getMetaData().getUserName();
62+
if (user != null && user.trim().equals("")) {
63+
user = null;
64+
}
65+
connectionInfo.put(connection, new DBInfo(sanitizedURL, type, user));
66+
}
67+
} catch (Throwable t) {
68+
// object may not be fully initialized.
69+
// calling constructor will populate map
70+
}
71+
}
72+
}
73+
74+
@Data
75+
public static class DBInfo {
76+
public static DBInfo UNKNOWN = new DBInfo("null", "unknown", null);
77+
private final String url;
78+
private final String type;
79+
private final String user;
80+
}
4681
}

dd-java-agent/src/main/java/com/datadoghq/agent/instrumentation/jdbc/DriverInstrumentation.java

Lines changed: 0 additions & 60 deletions
This file was deleted.

dd-java-agent/src/main/java/com/datadoghq/agent/instrumentation/jdbc/PreparedStatementInstrumentation.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ public static ActiveSpan startSpan(@Advice.This final PreparedStatement statemen
5151
return NoopActiveSpanSource.NoopActiveSpan.INSTANCE;
5252
}
5353

54-
DriverInstrumentation.DBInfo dbInfo = DriverInstrumentation.connectionInfo.get(connection);
54+
ConnectionInstrumentation.DBInfo dbInfo =
55+
ConnectionInstrumentation.connectionInfo.get(connection);
5556
if (dbInfo == null) {
56-
dbInfo = DriverInstrumentation.DBInfo.UNKNOWN;
57+
dbInfo = ConnectionInstrumentation.DBInfo.UNKNOWN;
5758
}
5859

5960
final ActiveSpan span =

dd-java-agent/src/main/java/com/datadoghq/agent/instrumentation/jdbc/StatementInstrumentation.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ public static ActiveSpan startSpan(
5050
return NoopActiveSpanSource.NoopActiveSpan.INSTANCE;
5151
}
5252

53-
DriverInstrumentation.DBInfo dbInfo = DriverInstrumentation.connectionInfo.get(connection);
53+
ConnectionInstrumentation.DBInfo dbInfo =
54+
ConnectionInstrumentation.connectionInfo.get(connection);
5455
if (dbInfo == null) {
55-
dbInfo = DriverInstrumentation.DBInfo.UNKNOWN;
56+
dbInfo = ConnectionInstrumentation.DBInfo.UNKNOWN;
5657
}
5758

5859
final ActiveSpan span =

0 commit comments

Comments
 (0)