Merge pull request #866 from DataDog/tyler/jdbc-instance

Attempt to properly parse out instance name from JDBC url
This commit is contained in:
Tyler Benson 2019-06-13 08:15:02 -07:00 committed by GitHub
commit ec3b586c2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1229 additions and 141 deletions

View File

@ -260,7 +260,10 @@ public class ReferenceCreator extends ClassVisitor {
// * DONE field-source class (descriptor) // * DONE field-source class (descriptor)
// * DONE field-source visibility from this point (PRIVATE?) // * DONE field-source visibility from this point (PRIVATE?)
final Type ownerType = Type.getType("L" + owner + ";"); final Type ownerType =
owner.startsWith("[")
? underlyingType(Type.getType(owner))
: Type.getType("L" + owner + ";");
final Type fieldType = Type.getType(descriptor); final Type fieldType = Type.getType(descriptor);
final List<Reference.Flag> fieldFlags = new ArrayList<>(); final List<Reference.Flag> fieldFlags = new ArrayList<>();
@ -334,7 +337,10 @@ public class ReferenceCreator extends ClassVisitor {
} }
} }
final Type ownerType = Type.getType("L" + owner + ";"); final Type ownerType =
owner.startsWith("[")
? underlyingType(Type.getType(owner))
: Type.getType("L" + owner + ";");
final List<Reference.Flag> methodFlags = new ArrayList<>(); final List<Reference.Flag> methodFlags = new ArrayList<>();
methodFlags.add( methodFlags.add(

View File

@ -26,16 +26,12 @@ class SpringJpaTest extends AgentTestRunner {
!repo.findAll().iterator().hasNext() !repo.findAll().iterator().hasNext()
assertTraces(1) { assertTraces(1) {
trace(0, 2) { trace(0, 1) {
span(0) { span(0) {
serviceName "hsqldb" serviceName "hsqldb"
resourceName "select customer0_.id as id1_0_, customer0_.firstName as firstNam2_0_, customer0_.lastName as lastName3_0_ from Customer customer0_"
spanType "sql" spanType "sql"
} }
span(1) {
serviceName "hsqldb"
spanType "sql"
childOf(span(0))
}
} }
} }
TEST_WRITER.clear() TEST_WRITER.clear()
@ -49,25 +45,22 @@ class SpringJpaTest extends AgentTestRunner {
// Behavior changed in new version: // Behavior changed in new version:
def extraTrace = TEST_WRITER.size() == 2 def extraTrace = TEST_WRITER.size() == 2
assertTraces(extraTrace ? 2 : 1) { assertTraces(extraTrace ? 2 : 1) {
trace(0, 2) {
span(0) {
serviceName "hsqldb"
spanType "sql"
}
span(1) {
serviceName "hsqldb"
spanType "sql"
childOf(span(0))
}
}
if (extraTrace) { if (extraTrace) {
trace(1, 1) { trace(0, 1) {
span(0) { span(0) {
serviceName "hsqldb" serviceName "hsqldb"
resourceName "call next value for hibernate_sequence"
spanType "sql" spanType "sql"
} }
} }
} }
trace(extraTrace ? 1 : 0, 1) {
span(0) {
serviceName "hsqldb"
resourceName ~/insert into Customer \(.*\) values \(.*, \?, \?\)/
spanType "sql"
}
}
} }
TEST_WRITER.clear() TEST_WRITER.clear()
@ -78,20 +71,17 @@ class SpringJpaTest extends AgentTestRunner {
then: then:
customer.id == savedId customer.id == savedId
assertTraces(2) { assertTraces(2) {
trace(0, 2) { trace(0, 1) {
span(0) { span(0) {
serviceName "hsqldb" serviceName "hsqldb"
resourceName "select customer0_.id as id1_0_0_, customer0_.firstName as firstNam2_0_0_, customer0_.lastName as lastName3_0_0_ from Customer customer0_ where customer0_.id=?"
spanType "sql" spanType "sql"
} }
span(1) {
serviceName "hsqldb"
spanType "sql"
childOf(span(0))
}
} }
trace(1, 1) { trace(1, 1) {
span(0) { span(0) {
serviceName "hsqldb" serviceName "hsqldb"
resourceName "update Customer set firstName=?, lastName=? where id=?"
spanType "sql" spanType "sql"
} }
} }
@ -105,16 +95,12 @@ class SpringJpaTest extends AgentTestRunner {
customer.id == savedId customer.id == savedId
customer.firstName == "Bill" customer.firstName == "Bill"
assertTraces(1) { assertTraces(1) {
trace(0, 2) { trace(0, 1) {
span(0) { span(0) {
serviceName "hsqldb" serviceName "hsqldb"
resourceName "select customer0_.id as id1_0_, customer0_.firstName as firstNam2_0_, customer0_.lastName as lastName3_0_ from Customer customer0_ where customer0_.lastName=?"
spanType "sql" spanType "sql"
} }
span(1) {
serviceName "hsqldb"
spanType "sql"
childOf(span(0))
}
} }
} }
TEST_WRITER.clear() TEST_WRITER.clear()
@ -124,20 +110,17 @@ class SpringJpaTest extends AgentTestRunner {
then: then:
assertTraces(2) { assertTraces(2) {
trace(0, 2) { trace(0, 1) {
span(0) { span(0) {
serviceName "hsqldb" serviceName "hsqldb"
resourceName "select customer0_.id as id1_0_0_, customer0_.firstName as firstNam2_0_0_, customer0_.lastName as lastName3_0_0_ from Customer customer0_ where customer0_.id=?"
spanType "sql" spanType "sql"
} }
span(1) {
serviceName "hsqldb"
spanType "sql"
childOf(span(0))
}
} }
trace(1, 1) { trace(1, 1) {
span(0) { span(0) {
serviceName "hsqldb" serviceName "hsqldb"
resourceName "delete from Customer where id=?"
spanType "sql" spanType "sql"
} }
} }

View File

@ -42,7 +42,7 @@ class SlickTest extends AgentTestRunner {
"$Tags.DB_TYPE.key" SlickUtils.Driver() "$Tags.DB_TYPE.key" SlickUtils.Driver()
"$Tags.DB_USER.key" SlickUtils.Username() "$Tags.DB_USER.key" SlickUtils.Username()
"db.instance" SlickUtils.Url() "db.instance" SlickUtils.Db()
"span.origin.type" "org.h2.jdbc.JdbcPreparedStatement" "span.origin.type" "org.h2.jdbc.JdbcPreparedStatement"
defaultTags() defaultTags()

View File

@ -35,8 +35,9 @@ class SlickUtils {
object SlickUtils { object SlickUtils {
val Driver = "h2" val Driver = "h2"
val Db = "test"
val Username = "TESTUSER" val Username = "TESTUSER"
val Url = s"jdbc:${Driver}:mem:test" val Url = s"jdbc:${Driver}:mem:${Db}"
val TestValue = 3 val TestValue = 3
val TestQuery = "SELECT 3" val TestQuery = "SELECT 3"

View File

@ -0,0 +1,18 @@
package datadog.trace.instrumentation.jdbc;
import lombok.Builder;
import lombok.Data;
@Data
@Builder(builderClassName = "Builder", toBuilder = true)
public class DBInfo {
public static DBInfo DEFAULT = new Builder().type("database").build();
private final String type;
private final String subtype;
private final String url;
private final String user;
private final String instance;
private final String db;
private final String host;
private final Integer port;
}

View File

@ -0,0 +1,71 @@
package datadog.trace.instrumentation.jdbc;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isInterface;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(Instrumenter.class)
public final class DriverInstrumentation extends Instrumenter.Default {
public DriverInstrumentation() {
super("jdbc");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return not(isInterface()).and(safeHasSuperType(named("java.sql.Driver")));
}
@Override
public String[] helperClassNames() {
final List<String> helpers = new ArrayList<>(JDBCConnectionUrlParser.values().length + 4);
helpers.add(packageName + ".DBInfo");
helpers.add(packageName + ".DBInfo$Builder");
helpers.add(packageName + ".JDBCMaps");
helpers.add(packageName + ".JDBCConnectionUrlParser");
for (final JDBCConnectionUrlParser parser : JDBCConnectionUrlParser.values()) {
helpers.add(parser.getClass().getName());
}
return helpers.toArray(new String[0]);
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
nameStartsWith("connect")
.and(takesArgument(0, String.class))
.and(takesArgument(1, Properties.class))
.and(returns(Connection.class)),
DriverAdvice.class.getName());
}
public static class DriverAdvice {
@Advice.OnMethodExit(suppress = Throwable.class)
public static void addDBInfo(
@Advice.Argument(0) final String url,
@Advice.Argument(1) final Properties props,
@Advice.Return final Connection connection) {
final DBInfo dbInfo = JDBCConnectionUrlParser.parse(url, props);
JDBCMaps.connectionInfo.put(connection, dbInfo);
}
}
}

View File

@ -0,0 +1,802 @@
package datadog.trace.instrumentation.jdbc;
import static datadog.trace.instrumentation.jdbc.DBInfo.DEFAULT;
import datadog.trace.bootstrap.ExceptionLogger;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Structured as an enum instead of a class hierarchy to allow iterating through the parsers
* automatically without having to maintain a separate list of parsers.
*/
public enum JDBCConnectionUrlParser {
GENERIC_URL_LIKE() {
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
try {
// Attempt generic parsing
final URI uri = new URI(jdbcUrl);
populateStandardProperties(builder, splitQuery(uri.getQuery(), "&"));
final String user = uri.getUserInfo();
if (user != null) {
builder.user(user);
}
String path = uri.getPath();
if (path.startsWith("/")) {
path = path.substring(1);
}
if (!path.isEmpty()) {
builder.db(path);
}
if (uri.getHost() != null) {
builder.host(uri.getHost());
}
if (uri.getPort() > 0) {
builder.port(uri.getPort());
}
return builder.type(uri.getScheme());
} catch (final Exception e) {
return builder;
}
}
},
MODIFIED_URL_LIKE() {
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final String type;
String serverName = "";
Integer port = null;
String instanceName = null;
final String user = null;
final int hostIndex = jdbcUrl.indexOf("://");
if (hostIndex <= 0) {
return builder;
}
type = jdbcUrl.substring(0, hostIndex);
final String[] split;
if (type.equals("db2") || type.equals("as400")) {
if (jdbcUrl.contains("=")) {
final int paramLoc = jdbcUrl.lastIndexOf(":");
split = new String[] {jdbcUrl.substring(0, paramLoc), jdbcUrl.substring(paramLoc + 1)};
} else {
split = new String[] {jdbcUrl};
}
} else {
split = jdbcUrl.split(";", 2);
}
if (split.length > 1) {
final Map<String, String> props = splitQuery(split[1], ";");
populateStandardProperties(builder, props);
if (props.containsKey("servername")) {
serverName = props.get("servername");
}
}
final String urlServerName = split[0].substring(hostIndex + 3);
if (!urlServerName.isEmpty()) {
serverName = urlServerName;
}
int instanceLoc = serverName.indexOf("/");
if (instanceLoc > 1) {
instanceName = serverName.substring(instanceLoc + 1);
serverName = serverName.substring(0, instanceLoc);
}
final int portLoc = serverName.indexOf(":");
if (portLoc > 1) {
port = Integer.parseInt(serverName.substring(portLoc + 1));
serverName = serverName.substring(0, portLoc);
}
instanceLoc = serverName.indexOf("\\");
if (instanceLoc > 1) {
instanceName = serverName.substring(instanceLoc + 1);
serverName = serverName.substring(0, instanceLoc);
}
if (instanceName != null) {
builder.instance(instanceName);
}
if (!serverName.isEmpty()) {
builder.host(serverName);
}
if (port != null) {
builder.port(port);
}
if (user != null) {
builder.user(user);
}
return builder.type(type);
}
},
POSTGRES("postgresql") {
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 5432;
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final DBInfo dbInfo = builder.build();
if (dbInfo.getHost() == null) {
builder.host(DEFAULT_HOST);
}
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
return GENERIC_URL_LIKE.doParse(jdbcUrl, builder);
}
},
MYSQL("mysql", "mariadb") {
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 3306;
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final DBInfo dbInfo = builder.build();
if (dbInfo.getHost() == null) {
builder.host(DEFAULT_HOST);
}
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
final int protoLoc = jdbcUrl.indexOf("://");
final int typeEndLoc = dbInfo.getType().length();
if (protoLoc > typeEndLoc) {
return MARIA_SUBPROTO
.doParse(jdbcUrl.substring(protoLoc + 3), builder)
.subtype(jdbcUrl.substring(typeEndLoc + 1, protoLoc));
}
if (protoLoc > 0) {
return GENERIC_URL_LIKE.doParse(jdbcUrl, builder);
}
final int hostEndLoc;
final int portLoc = jdbcUrl.indexOf(":", typeEndLoc + 1);
final int dbLoc = jdbcUrl.indexOf("/", typeEndLoc);
final int paramLoc = jdbcUrl.indexOf("?", dbLoc);
if (paramLoc > 0) {
populateStandardProperties(builder, splitQuery(jdbcUrl.substring(paramLoc + 1), "&"));
builder.db(jdbcUrl.substring(dbLoc + 1, paramLoc));
} else {
builder.db(jdbcUrl.substring(dbLoc + 1));
}
if (portLoc > 0) {
hostEndLoc = portLoc;
try {
builder.port(Integer.parseInt(jdbcUrl.substring(portLoc + 1, dbLoc)));
} catch (final NumberFormatException e) {
}
} else {
hostEndLoc = dbLoc;
}
builder.host(jdbcUrl.substring(typeEndLoc + 1, hostEndLoc));
return builder;
}
},
MARIA_SUBPROTO() {
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final int hostEndLoc;
final int clusterSepLoc = jdbcUrl.indexOf(",");
final int ipv6End = jdbcUrl.startsWith("[") ? jdbcUrl.indexOf("]") : -1;
int portLoc = jdbcUrl.indexOf(":", Math.max(0, ipv6End));
portLoc = clusterSepLoc < portLoc ? -1 : portLoc;
final int dbLoc = jdbcUrl.indexOf("/", Math.max(portLoc, clusterSepLoc));
final int paramLoc = jdbcUrl.indexOf("?", dbLoc);
if (paramLoc > 0) {
populateStandardProperties(builder, splitQuery(jdbcUrl.substring(paramLoc + 1), "&"));
builder.db(jdbcUrl.substring(dbLoc + 1, paramLoc));
} else {
builder.db(jdbcUrl.substring(dbLoc + 1));
}
if (jdbcUrl.startsWith("address=")) {
return MARIA_ADDRESS.doParse(jdbcUrl, builder);
}
if (portLoc > 0) {
hostEndLoc = portLoc;
final int portEndLoc = clusterSepLoc > 0 ? clusterSepLoc : dbLoc;
try {
builder.port(Integer.parseInt(jdbcUrl.substring(portLoc + 1, portEndLoc)));
} catch (final NumberFormatException e) {
}
} else {
hostEndLoc = clusterSepLoc > 0 ? clusterSepLoc : dbLoc;
}
if (ipv6End > 0) {
builder.host(jdbcUrl.substring(1, ipv6End));
} else {
builder.host(jdbcUrl.substring(0, hostEndLoc));
}
return builder;
}
},
MARIA_ADDRESS() {
private final Pattern HOST_REGEX = Pattern.compile("\\(\\s*host\\s*=\\s*([^ )]+)\\s*\\)");
private final Pattern PORT_REGEX = Pattern.compile("\\(\\s*port\\s*=\\s*([\\d]+)\\s*\\)");
private final Pattern USER_REGEX = Pattern.compile("\\(\\s*user\\s*=\\s*([^ )]+)\\s*\\)");
@Override
DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder) {
final int addressEnd = jdbcUrl.indexOf(",address=");
if (addressEnd > 0) {
jdbcUrl = jdbcUrl.substring(0, addressEnd);
}
final Matcher hostMatcher = HOST_REGEX.matcher(jdbcUrl);
if (hostMatcher.find()) {
builder.host(hostMatcher.group(1));
}
final Matcher portMatcher = PORT_REGEX.matcher(jdbcUrl);
if (portMatcher.find()) {
builder.port(Integer.parseInt(portMatcher.group(1)));
}
final Matcher userMatcher = USER_REGEX.matcher(jdbcUrl);
if (userMatcher.find()) {
builder.user(userMatcher.group(1));
}
return builder;
}
},
SAP("sap") {
private static final String DEFAULT_HOST = "localhost";
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final DBInfo dbInfo = builder.build();
if (dbInfo.getHost() == null) {
builder.host(DEFAULT_HOST);
}
return GENERIC_URL_LIKE.doParse(jdbcUrl, builder);
}
},
MSSQLSERVER("microsoft", "sqlserver") {
private static final String DEFAULT_HOST = "localhost";
private static final int DEFAULT_PORT = 1433;
private static final String DEFAULT_INSTANCE = "MSSQLSERVER";
@Override
DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder) {
if (jdbcUrl.startsWith("microsoft:")) {
jdbcUrl = jdbcUrl.substring("microsoft:".length());
}
if (!jdbcUrl.startsWith("sqlserver://")) {
return builder;
}
builder.type("sqlserver");
final DBInfo dbInfo = builder.build();
if (dbInfo.getInstance() == null) {
builder.instance(DEFAULT_INSTANCE);
}
if (dbInfo.getHost() == null) {
builder.host(DEFAULT_HOST);
}
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder);
}
},
DB2("db2", "as400") {
private static final int DEFAULT_PORT = 50000;
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final DBInfo dbInfo = builder.build();
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder);
}
},
ORACLE("oracle") {
private static final int DEFAULT_PORT = 1521;
@Override
DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder) {
final int typeEndIndex = jdbcUrl.indexOf(":", "oracle:".length());
final String subtype = jdbcUrl.substring("oracle:".length(), typeEndIndex);
jdbcUrl = jdbcUrl.substring(typeEndIndex + 1);
builder.subtype(subtype);
final DBInfo dbInfo = builder.build();
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
if (jdbcUrl.contains("@")) {
return ORACLE_AT.doParse(jdbcUrl, builder);
} else {
return ORACLE_CONNECT_INFO.doParse(jdbcUrl, builder);
}
}
},
ORACLE_CONNECT_INFO() {
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final String host;
final Integer port;
final String instance;
final int hostEnd = jdbcUrl.indexOf(":");
final int instanceLoc = jdbcUrl.indexOf("/");
if (hostEnd > 0) {
host = jdbcUrl.substring(0, hostEnd);
final int afterHostEnd = jdbcUrl.indexOf(":", hostEnd + 1);
if (afterHostEnd > 0) {
port = Integer.parseInt(jdbcUrl.substring(hostEnd + 1, afterHostEnd));
instance = jdbcUrl.substring(afterHostEnd + 1);
} else {
if (instanceLoc > 0) {
instance = jdbcUrl.substring(instanceLoc + 1);
port = Integer.parseInt(jdbcUrl.substring(hostEnd + 1, instanceLoc));
} else {
final String portOrInstance = jdbcUrl.substring(hostEnd + 1);
Integer parsedPort = null;
try {
parsedPort = Integer.parseInt(portOrInstance);
} catch (final NumberFormatException e) {
}
if (parsedPort == null) {
port = null;
instance = portOrInstance;
} else {
port = parsedPort;
instance = null;
}
}
}
} else {
if (instanceLoc > 0) {
host = jdbcUrl.substring(0, instanceLoc);
port = null;
instance = jdbcUrl.substring(instanceLoc + 1);
} else {
if (jdbcUrl.isEmpty()) {
return builder;
} else {
host = null;
port = null;
instance = jdbcUrl;
}
}
}
if (host != null) {
builder.host(host);
}
if (port != null) {
builder.port(port);
}
return builder.instance(instance);
}
},
ORACLE_AT() {
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
if (jdbcUrl.contains("@(description")) {
return ORACLE_AT_DESCRIPTION.doParse(jdbcUrl, builder);
}
final String user;
final String[] atSplit = jdbcUrl.split("@", 2);
final int userInfoLoc = atSplit[0].indexOf("/");
if (userInfoLoc > 0) {
user = atSplit[0].substring(0, userInfoLoc);
} else {
user = null;
}
final String connectInfo = atSplit[1];
final int hostStart;
if (connectInfo.startsWith("//")) {
hostStart = "//".length();
} else if (connectInfo.startsWith("ldap://")) {
hostStart = "ldap://".length();
} else {
hostStart = 0;
}
if (user != null) {
builder.user(user);
}
return ORACLE_CONNECT_INFO.doParse(connectInfo.substring(hostStart), builder);
}
},
/**
* This parser can locate incorrect data if multiple addresses are defined but not everything is
* defined in the first block. (It would locate data from subsequent address blocks.
*/
ORACLE_AT_DESCRIPTION() {
private final Pattern HOST_REGEX = Pattern.compile("\\(\\s*host\\s*=\\s*([^ )]+)\\s*\\)");
private final Pattern PORT_REGEX = Pattern.compile("\\(\\s*port\\s*=\\s*([\\d]+)\\s*\\)");
private final Pattern INSTANCE_REGEX =
Pattern.compile("\\(\\s*service_name\\s*=\\s*([^ )]+)\\s*\\)");
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final String[] atSplit = jdbcUrl.split("@", 2);
final int userInfoLoc = atSplit[0].indexOf("/");
if (userInfoLoc > 0) {
builder.user(atSplit[0].substring(0, userInfoLoc));
}
final Matcher hostMatcher = HOST_REGEX.matcher(atSplit[1]);
if (hostMatcher.find()) {
builder.host(hostMatcher.group(1));
}
final Matcher portMatcher = PORT_REGEX.matcher(atSplit[1]);
if (portMatcher.find()) {
builder.port(Integer.parseInt(portMatcher.group(1)));
}
final Matcher instanceMatcher = INSTANCE_REGEX.matcher(atSplit[1]);
if (instanceMatcher.find()) {
builder.instance(instanceMatcher.group(1));
}
return builder;
}
},
H2("h2") {
private static final int DEFAULT_PORT = 8082;
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
final String instance;
final String h2Url = jdbcUrl.substring("h2:".length());
if (h2Url.startsWith("mem:")) {
builder.subtype("mem");
final int propLoc = h2Url.indexOf(";");
if (propLoc >= 0) {
instance = h2Url.substring("mem:".length(), propLoc);
} else {
instance = h2Url.substring("mem:".length());
}
} else if (h2Url.startsWith("file:")) {
builder.subtype("file");
final int propLoc = h2Url.indexOf(";");
if (propLoc >= 0) {
instance = h2Url.substring("file:".length(), propLoc);
} else {
instance = h2Url.substring("file:".length());
}
} else if (h2Url.startsWith("zip:")) {
builder.subtype("zip");
final int propLoc = h2Url.indexOf(";");
if (propLoc >= 0) {
instance = h2Url.substring("zip:".length(), propLoc);
} else {
instance = h2Url.substring("zip:".length());
}
} else if (h2Url.startsWith("tcp:")) {
final DBInfo dbInfo = builder.build();
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("h2").subtype("tcp");
} else if (h2Url.startsWith("ssl:")) {
final DBInfo dbInfo = builder.build();
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("h2").subtype("ssl");
} else {
builder.subtype("file");
final int propLoc = h2Url.indexOf(";");
if (propLoc >= 0) {
instance = h2Url.substring(0, propLoc);
} else {
instance = h2Url;
}
}
if (!instance.isEmpty()) {
builder.instance(instance);
}
return builder;
}
},
HSQL("hsqldb") {
private static final String DEFAULT_USER = "SA";
private static final int DEFAULT_PORT = 9001;
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
String instance = null;
final DBInfo dbInfo = builder.build();
if (dbInfo.getUser() == null) {
builder.user(DEFAULT_USER);
}
final String hsqlUrl = jdbcUrl.substring("hsqldb:".length());
if (hsqlUrl.startsWith("mem:")) {
builder.subtype("mem");
instance = hsqlUrl.substring("mem:".length());
} else if (hsqlUrl.startsWith("file:")) {
builder.subtype("file");
instance = hsqlUrl.substring("file:".length());
} else if (hsqlUrl.startsWith("res:")) {
builder.subtype("res");
instance = hsqlUrl.substring("res:".length());
} else if (hsqlUrl.startsWith("hsql:")) {
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb").subtype("hsql");
} else if (hsqlUrl.startsWith("hsqls:")) {
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb").subtype("hsqls");
} else if (hsqlUrl.startsWith("http:")) {
if (dbInfo.getPort() == null) {
builder.port(80);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb").subtype("http");
} else if (hsqlUrl.startsWith("https:")) {
if (dbInfo.getPort() == null) {
builder.port(443);
}
return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb").subtype("https");
} else {
builder.subtype("mem");
instance = hsqlUrl;
}
return builder.instance(instance);
}
},
DERBY("derby") {
private static final String DEFAULT_USER = "APP";
private static final int DEFAULT_PORT = 1527;
@Override
DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) {
String instance = null;
String host = null;
final DBInfo dbInfo = builder.build();
if (dbInfo.getUser() == null) {
builder.user(DEFAULT_USER);
}
final String derbyUrl = jdbcUrl.substring("derby:".length());
final String[] split = derbyUrl.split(";", 2);
if (split.length > 1) {
populateStandardProperties(builder, splitQuery(split[1], ";"));
}
final String details = split[0];
if (details.startsWith("memory:")) {
builder.subtype("memory");
final String urlInstance = details.substring("memory:".length());
if (!urlInstance.isEmpty()) {
instance = urlInstance;
}
} else if (details.startsWith("directory:")) {
builder.subtype("directory");
final String urlInstance = details.substring("directory:".length());
if (!urlInstance.isEmpty()) {
instance = urlInstance;
}
} else if (details.startsWith("classpath:")) {
builder.subtype("classpath");
final String urlInstance = details.substring("classpath:".length());
if (!urlInstance.isEmpty()) {
instance = urlInstance;
}
} else if (details.startsWith("jar:")) {
builder.subtype("jar");
final String urlInstance = details.substring("jar:".length());
if (!urlInstance.isEmpty()) {
instance = urlInstance;
}
} else if (details.startsWith("//")) {
builder.subtype("network");
if (dbInfo.getPort() == null) {
builder.port(DEFAULT_PORT);
}
String url = details.substring("//".length());
final int instanceLoc = url.indexOf("/");
if (instanceLoc >= 0) {
instance = url.substring(instanceLoc + 1);
final int protoLoc = instance.indexOf(":");
if (protoLoc >= 0) {
instance = instance.substring(protoLoc + 1);
}
url = url.substring(0, instanceLoc);
}
final int portLoc = url.indexOf(":");
if (portLoc > 0) {
host = url.substring(0, portLoc);
builder.port(Integer.parseInt(url.substring(portLoc + 1)));
} else {
host = url;
}
} else {
builder.subtype("directory");
final String urlInstance = details;
if (!urlInstance.isEmpty()) {
instance = urlInstance;
}
}
if (host != null) {
builder.host(host);
}
return builder.instance(instance);
}
};
private static final Map<String, JDBCConnectionUrlParser> typeParsers = new HashMap<>();
static {
for (final JDBCConnectionUrlParser parser : JDBCConnectionUrlParser.values()) {
for (final String key : parser.typeKeys) {
typeParsers.put(key, parser);
}
}
}
private final String[] typeKeys;
JDBCConnectionUrlParser(final String... typeKeys) {
this.typeKeys = typeKeys;
}
abstract DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder);
public static DBInfo parse(String connectionUrl, final Properties props) {
if (connectionUrl == null) {
return DEFAULT;
}
// Make this easier and ignore case.
connectionUrl = connectionUrl.toLowerCase();
if (!connectionUrl.startsWith("jdbc:")) {
return DEFAULT;
}
final String jdbcUrl = connectionUrl.substring("jdbc:".length());
final int typeLoc = jdbcUrl.indexOf(':');
if (typeLoc < 1) {
// Invalid format: `jdbc:` or `jdbc::`
return DEFAULT;
}
final String baseType = jdbcUrl.substring(0, typeLoc);
final DBInfo.Builder parsedProps = DEFAULT.toBuilder().type(baseType);
populateStandardProperties(parsedProps, props);
try {
if (typeParsers.containsKey(baseType)) {
// Delegate to specific parser
return typeParsers.get(baseType).doParse(jdbcUrl, parsedProps).build();
}
return GENERIC_URL_LIKE.doParse(connectionUrl, parsedProps).build();
} catch (final Exception e) {
ExceptionLogger.LOGGER.debug("Error parsing URL", e);
return parsedProps.build();
}
}
// Source: https://stackoverflow.com/a/13592567
private static Map<String, String> splitQuery(final String query, final String separator) {
if (query == null || query.isEmpty()) {
return Collections.emptyMap();
}
final Map<String, String> query_pairs = new LinkedHashMap<>();
final String[] pairs = query.split(separator);
for (final String pair : pairs) {
try {
final int idx = pair.indexOf("=");
final String key = idx > 0 ? URLDecoder.decode(pair.substring(0, idx), "UTF-8") : pair;
if (!query_pairs.containsKey(key)) {
final String value =
idx > 0 && pair.length() > idx + 1
? URLDecoder.decode(pair.substring(idx + 1), "UTF-8")
: null;
query_pairs.put(key, value);
}
} catch (final UnsupportedEncodingException e) {
// Ignore.
}
}
return query_pairs;
}
private static void populateStandardProperties(
final DBInfo.Builder builder, final Map<? extends Object, ? extends Object> props) {
if (props != null && !props.isEmpty()) {
if (props.containsKey("user")) {
builder.user((String) props.get("user"));
}
if (props.containsKey("databasename")) {
builder.db((String) props.get("databasename"));
}
if (props.containsKey("databaseName")) {
builder.db((String) props.get("databaseName"));
}
if (props.containsKey("servername")) {
builder.host((String) props.get("servername"));
}
if (props.containsKey("serverName")) {
builder.host((String) props.get("serverName"));
}
if (props.containsKey("portnumber")) {
final String portNumber = (String) props.get("portnumber");
try {
builder.port(Integer.parseInt(portNumber));
} catch (final NumberFormatException e) {
ExceptionLogger.LOGGER.debug("Error parsing portnumber property: " + portNumber, e);
}
}
if (props.containsKey("portNumber")) {
final String portNumber = (String) props.get("portNumber");
try {
builder.port(Integer.parseInt(portNumber));
} catch (final NumberFormatException e) {
ExceptionLogger.LOGGER.debug("Error parsing portNumber property: " + portNumber, e);
}
}
}
}
}

View File

@ -10,9 +10,11 @@ import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
public class JDBCDecorator extends DatabaseClientDecorator<JDBCMaps.DBInfo> { public class JDBCDecorator extends DatabaseClientDecorator<DBInfo> {
public static final JDBCDecorator DECORATE = new JDBCDecorator(); public static final JDBCDecorator DECORATE = new JDBCDecorator();
private static final String DB_QUERY = "DB Query";
@Override @Override
protected String[] instrumentationNames() { protected String[] instrumentationNames() {
return new String[] {"jdbc"}; return new String[] {"jdbc"};
@ -39,22 +41,23 @@ public class JDBCDecorator extends DatabaseClientDecorator<JDBCMaps.DBInfo> {
} }
@Override @Override
protected String dbUser(final JDBCMaps.DBInfo info) { protected String dbUser(final DBInfo info) {
return info.getUser(); return info.getUser();
} }
@Override @Override
protected String dbInstance(final JDBCMaps.DBInfo info) { protected String dbInstance(final DBInfo info) {
return info.getUrl(); return info.getInstance();
} }
public Span onConnection(final Span span, final Connection connection) { public Span onConnection(final Span span, final Connection connection) {
JDBCMaps.DBInfo dbInfo = JDBCMaps.connectionInfo.get(connection); DBInfo dbInfo = JDBCMaps.connectionInfo.get(connection);
/** /**
* Logic to get the DBInfo from a JDBC Connection, if the connection was never seen before, the * Logic to get the DBInfo from a JDBC Connection, if the connection was not created via
* connectionInfo map will return null and will attempt to extract DBInfo from the connection. * Driver.connect, or it has never seen before, the connectionInfo map will return null and will
* If the DBInfo can't be extracted, then the connection will be stored with the DEFAULT DBInfo * attempt to extract DBInfo from the connection. If the DBInfo can't be extracted, then the
* as the value in the connectionInfo map to avoid retry overhead. * connection will be stored with the DEFAULT DBInfo as the value in the connectionInfo map to
* avoid retry overhead.
*/ */
{ {
if (dbInfo == null) { if (dbInfo == null) {
@ -62,19 +65,12 @@ public class JDBCDecorator extends DatabaseClientDecorator<JDBCMaps.DBInfo> {
final DatabaseMetaData metaData = connection.getMetaData(); final DatabaseMetaData metaData = connection.getMetaData();
final String url = metaData.getURL(); final String url = metaData.getURL();
if (url != null) { if (url != null) {
// Remove end of url to prevent passwords from leaking: dbInfo = JDBCConnectionUrlParser.parse(url, connection.getClientInfo());
final String sanitizedURL = url.replaceAll("[?;].*", "");
final String type = url.split(":", -1)[1];
String user = metaData.getUserName();
if (user != null && user.trim().equals("")) {
user = null;
}
dbInfo = new JDBCMaps.DBInfo(sanitizedURL, type, user);
} else { } else {
dbInfo = JDBCMaps.DBInfo.DEFAULT; dbInfo = DBInfo.DEFAULT;
} }
} catch (final SQLException se) { } catch (final SQLException se) {
dbInfo = JDBCMaps.DBInfo.DEFAULT; dbInfo = DBInfo.DEFAULT;
} }
JDBCMaps.connectionInfo.put(connection, dbInfo); JDBCMaps.connectionInfo.put(connection, dbInfo);
} }
@ -89,7 +85,7 @@ public class JDBCDecorator extends DatabaseClientDecorator<JDBCMaps.DBInfo> {
@Override @Override
public Span onStatement(final Span span, final String statement) { public Span onStatement(final Span span, final String statement) {
final String resourceName = statement == null ? JDBCMaps.DB_QUERY : statement; final String resourceName = statement == null ? DB_QUERY : statement;
span.setTag(DDTags.RESOURCE_NAME, resourceName); span.setTag(DDTags.RESOURCE_NAME, resourceName);
Tags.COMPONENT.set(span, "java-jdbc-statement"); Tags.COMPONENT.set(span, "java-jdbc-statement");
return super.onStatement(span, statement); return super.onStatement(span, statement);
@ -97,7 +93,7 @@ public class JDBCDecorator extends DatabaseClientDecorator<JDBCMaps.DBInfo> {
public Span onPreparedStatement(final Span span, final PreparedStatement statement) { public Span onPreparedStatement(final Span span, final PreparedStatement statement) {
final String sql = JDBCMaps.preparedStatements.get(statement); final String sql = JDBCMaps.preparedStatements.get(statement);
final String resourceName = sql == null ? JDBCMaps.DB_QUERY : sql; final String resourceName = sql == null ? DB_QUERY : sql;
span.setTag(DDTags.RESOURCE_NAME, resourceName); span.setTag(DDTags.RESOURCE_NAME, resourceName);
Tags.COMPONENT.set(span, "java-jdbc-prepared_statement"); Tags.COMPONENT.set(span, "java-jdbc-prepared_statement");
return super.onStatement(span, sql); return super.onStatement(span, sql);

View File

@ -5,7 +5,6 @@ import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap;
import datadog.trace.bootstrap.WeakMap; import datadog.trace.bootstrap.WeakMap;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import lombok.Data;
/** /**
* JDBC instrumentation shares a global map of connection info. * JDBC instrumentation shares a global map of connection info.
@ -15,14 +14,4 @@ import lombok.Data;
public class JDBCMaps { public class JDBCMaps {
public static final WeakMap<Connection, DBInfo> connectionInfo = newWeakMap(); public static final WeakMap<Connection, DBInfo> connectionInfo = newWeakMap();
public static final WeakMap<PreparedStatement, String> preparedStatements = newWeakMap(); public static final WeakMap<PreparedStatement, String> preparedStatements = newWeakMap();
public static final String DB_QUERY = "DB Query";
@Data
public static class DBInfo {
public static DBInfo DEFAULT = new DBInfo("null", "database", null);
private final String url;
private final String type;
private final String user;
}
} }

View File

@ -1,10 +1,12 @@
package datadog.trace.instrumentation.jdbc; package datadog.trace.instrumentation.jdbc;
import datadog.trace.bootstrap.ExceptionLogger; import datadog.trace.bootstrap.ExceptionLogger;
import java.lang.reflect.Field;
import java.sql.Connection; import java.sql.Connection;
import java.sql.Statement; import java.sql.Statement;
public abstract class JDBCUtils { public abstract class JDBCUtils {
private static Field c3poField = null;
/** /**
* @param statement * @param statement
@ -14,6 +16,13 @@ public abstract class JDBCUtils {
Connection connection; Connection connection;
try { try {
connection = statement.getConnection(); connection = statement.getConnection();
if (c3poField != null) {
if (connection.getClass().getName().equals("com.mchange.v2.c3p0.impl.NewProxyConnection")) {
return (Connection) c3poField.get(connection);
}
}
try { try {
// unwrap the connection to cache the underlying actual connection and to not cache proxy // unwrap the connection to cache the underlying actual connection and to not cache proxy
// objects // objects
@ -21,6 +30,15 @@ public abstract class JDBCUtils {
connection = connection.unwrap(Connection.class); connection = connection.unwrap(Connection.class);
} }
} catch (final Exception | AbstractMethodError e) { } catch (final Exception | AbstractMethodError e) {
// Attempt to work around c3po delegating to an connection that doesn't support unwrapping.
final Class<? extends Connection> connectionClass = connection.getClass();
if (connectionClass.getName().equals("com.mchange.v2.c3p0.impl.NewProxyConnection")) {
final Field inner = connectionClass.getDeclaredField("inner");
inner.setAccessible(true);
c3poField = inner;
return (Connection) c3poField.get(connection);
}
// perhaps wrapping isn't supported? // perhaps wrapping isn't supported?
// ex: org.h2.jdbc.JdbcConnection v1.3.175 // ex: org.h2.jdbc.JdbcConnection v1.3.175
// or: jdts.jdbc which always throws `AbstractMethodError` (at least up to version 1.3) // or: jdts.jdbc which always throws `AbstractMethodError` (at least up to version 1.3)

View File

@ -20,6 +20,8 @@ import io.opentracing.noop.NoopScopeManager;
import io.opentracing.util.GlobalTracer; import io.opentracing.util.GlobalTracer;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodDescription;
@ -40,15 +42,23 @@ public final class PreparedStatementInstrumentation extends Instrumenter.Default
@Override @Override
public String[] helperClassNames() { public String[] helperClassNames() {
return new String[] { final List<String> helpers = new ArrayList<>(JDBCConnectionUrlParser.values().length + 9);
"datadog.trace.agent.decorator.BaseDecorator",
"datadog.trace.agent.decorator.ClientDecorator", helpers.add(packageName + ".DBInfo");
"datadog.trace.agent.decorator.DatabaseClientDecorator", helpers.add(packageName + ".DBInfo$Builder");
packageName + ".JDBCDecorator", helpers.add(packageName + ".JDBCUtils");
packageName + ".JDBCMaps", helpers.add(packageName + ".JDBCMaps");
packageName + ".JDBCMaps$DBInfo", helpers.add(packageName + ".JDBCConnectionUrlParser");
packageName + ".JDBCUtils",
}; helpers.add("datadog.trace.agent.decorator.BaseDecorator");
helpers.add("datadog.trace.agent.decorator.ClientDecorator");
helpers.add("datadog.trace.agent.decorator.DatabaseClientDecorator");
helpers.add(packageName + ".JDBCDecorator");
for (final JDBCConnectionUrlParser parser : JDBCConnectionUrlParser.values()) {
helpers.add(parser.getClass().getName());
}
return helpers.toArray(new String[0]);
} }
@Override @Override

View File

@ -20,6 +20,8 @@ import io.opentracing.noop.NoopScopeManager;
import io.opentracing.util.GlobalTracer; import io.opentracing.util.GlobalTracer;
import java.sql.Connection; import java.sql.Connection;
import java.sql.Statement; import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import net.bytebuddy.asm.Advice; import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription; import net.bytebuddy.description.method.MethodDescription;
@ -40,15 +42,23 @@ public final class StatementInstrumentation extends Instrumenter.Default {
@Override @Override
public String[] helperClassNames() { public String[] helperClassNames() {
return new String[] { final List<String> helpers = new ArrayList<>(JDBCConnectionUrlParser.values().length + 9);
"datadog.trace.agent.decorator.BaseDecorator",
"datadog.trace.agent.decorator.ClientDecorator", helpers.add(packageName + ".DBInfo");
"datadog.trace.agent.decorator.DatabaseClientDecorator", helpers.add(packageName + ".DBInfo$Builder");
packageName + ".JDBCDecorator", helpers.add(packageName + ".JDBCUtils");
packageName + ".JDBCMaps", helpers.add(packageName + ".JDBCMaps");
packageName + ".JDBCMaps$DBInfo", helpers.add(packageName + ".JDBCConnectionUrlParser");
packageName + ".JDBCUtils",
}; helpers.add("datadog.trace.agent.decorator.BaseDecorator");
helpers.add("datadog.trace.agent.decorator.ClientDecorator");
helpers.add("datadog.trace.agent.decorator.DatabaseClientDecorator");
helpers.add(packageName + ".JDBCDecorator");
for (final JDBCConnectionUrlParser parser : JDBCConnectionUrlParser.values()) {
helpers.add(parser.getClass().getName());
}
return helpers.toArray(new String[0]);
} }
@Override @Override

View File

@ -0,0 +1,181 @@
import datadog.trace.instrumentation.jdbc.DBInfo
import spock.lang.Shared
import spock.lang.Specification
import static datadog.trace.instrumentation.jdbc.JDBCConnectionUrlParser.parse
class JDBCConnectionUrlParserTest extends Specification {
@Shared
def stdProps = {
def prop = new Properties()
// https://download.oracle.com/otn-pub/jcp/jdbc-4_1-mrel-spec/jdbc4.1-fr-spec.pdf
prop.setProperty("databaseName", "stdDatabaseName")
prop.setProperty("dataSourceName", "stdDatasourceName")
prop.setProperty("description", "Some description")
prop.setProperty("networkProtocol", "stdProto")
prop.setProperty("password", "PASSWORD!")
prop.setProperty("portNumber", "9999")
prop.setProperty("roleName", "stdRoleName")
prop.setProperty("serverName", "stdServerName")
prop.setProperty("user", "stdUserName")
return prop
}()
def "invalid url returns default"() {
expect:
parse(url, null) == DBInfo.DEFAULT
where:
url | _
null | _
"" | _
"jdbc:" | _
"jdbc::" | _
"bogus:string" | _
}
def "verify #type:#subtype parsing of #url"() {
setup:
def info = parse(url, props)
expect:
info.url == expected.url
info.type == expected.type
info.host == expected.host
info.port == expected.port
info.user == expected.user
info.instance == expected.instance
info == expected
where:
url | props | type | subtype | user | host | port | instance | db
// https://jdbc.postgresql.org/documentation/94/connect.html
"jdbc:postgresql:///" | null | "postgresql" | null | null | "localhost" | 5432 | null | null
"jdbc:postgresql:///" | stdProps | "postgresql" | null | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName"
"jdbc:postgresql://pg.host" | null | "postgresql" | null | null | "pg.host" | 5432 | null | null
"jdbc:postgresql://pg.host:11/pgdb?user=pguser&password=PW" | null | "postgresql" | null | "pguser" | "pg.host" | 11 | null | "pgdb"
"jdbc:postgresql://pg.host:11/pgdb?user=pguser&password=PW" | stdProps | "postgresql" | null | "pguser" | "pg.host" | 11 | null | "pgdb"
// https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-jdbc-url-format.html
// https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html
"jdbc:mysql:///" | null | "mysql" | null | null | "localhost" | 3306 | null | null
"jdbc:mysql:///" | stdProps | "mysql" | null | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName"
"jdbc:mysql://my.host" | null | "mysql" | null | null | "my.host" | 3306 | null | null
"jdbc:mysql://my.host?user=myuser&password=PW" | null | "mysql" | null | "myuser" | "my.host" | 3306 | null | null
"jdbc:mysql://my.host:22/mydb?user=myuser&password=PW" | null | "mysql" | null | "myuser" | "my.host" | 22 | null | "mydb"
"jdbc:mysql://127.0.0.1:22/mydb?user=myuser&password=PW" | stdProps | "mysql" | null | "myuser" | "127.0.0.1" | 22 | null | "mydb"
// https://mariadb.com/kb/en/library/about-mariadb-connector-j/#connection-strings
"jdbc:mariadb:127.0.0.1:33/mdbdb" | null | "mariadb" | null | null | "127.0.0.1" | 33 | null | "mdbdb"
"jdbc:mariadb:localhost/mdbdb" | null | "mariadb" | null | null | "localhost" | 3306 | null | "mdbdb"
"jdbc:mariadb:localhost/mdbdb?user=mdbuser&password=PW" | stdProps | "mariadb" | null | "mdbuser" | "localhost" | 9999 | null | "mdbdb"
"jdbc:mariadb:localhost:33/mdbdb" | stdProps | "mariadb" | null | "stdUserName" | "localhost" | 33 | null | "mdbdb"
"jdbc:mariadb://mdb.host:33/mdbdb?user=mdbuser&password=PW" | null | "mariadb" | null | "mdbuser" | "mdb.host" | 33 | null | "mdbdb"
"jdbc:mariadb:aurora://mdb.host/mdbdb" | null | "mariadb" | "aurora" | null | "mdb.host" | 3306 | null | "mdbdb"
"jdbc:mysql:aurora://mdb.host/mdbdb" | null | "mysql" | "aurora" | null | "mdb.host" | 3306 | null | "mdbdb"
"jdbc:mysql:failover://localhost/mdbdb?autoReconnect=true" | null | "mysql" | "failover" | null | "localhost" | 3306 | null | "mdbdb"
"jdbc:mariadb:failover://mdb.host1:33,mdb.host/mdbdb?characterEncoding=utf8" | null | "mariadb" | "failover" | null | "mdb.host1" | 33 | null | "mdbdb"
"jdbc:mariadb:sequential://mdb.host1,mdb.host2:33/mdbdb" | null | "mariadb" | "sequential" | null | "mdb.host1" | 3306 | null | "mdbdb"
"jdbc:mariadb:loadbalance://127.0.0.1:33,mdb.host/mdbdb" | null | "mariadb" | "loadbalance" | null | "127.0.0.1" | 33 | null | "mdbdb"
"jdbc:mariadb:loadbalance://[2001:0660:7401:0200:0000:0000:0edf:bdd7]:33,mdb.host/mdbdb" | null | "mariadb" | "loadbalance" | null | "2001:0660:7401:0200:0000:0000:0edf:bdd7" | 33 | null | "mdbdb"
"jdbc:mysql:loadbalance://127.0.0.1,127.0.0.1:3306/mdbdb?user=mdbuser&password=PW" | null | "mysql" | "loadbalance" | "mdbuser" | "127.0.0.1" | 3306 | null | "mdbdb"
"jdbc:mariadb:replication://localhost:33,anotherhost:3306/mdbdb" | null | "mariadb" | "replication" | null | "localhost" | 33 | null | "mdbdb"
"jdbc:mysql:replication://address=(HOST=127.0.0.1)(port=33)(user=mdbuser)(password=PW)," +
"address=(host=mdb.host)(port=3306)(user=otheruser)(password=PW)/mdbdb?user=wrong&password=PW" | null | "mysql" | "replication" | "mdbuser" | "127.0.0.1" | 33 | null | "mdbdb"
"jdbc:mysql:replication://address=(HOST=mdb.host)," +
"address=(host=anotherhost)(port=3306)(user=wrong)(password=PW)/mdbdb?user=mdbuser&password=PW" | null | "mysql" | "replication" | "mdbuser" | "mdb.host" | 3306 | null | "mdbdb"
//https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url
"jdbc:microsoft:sqlserver://;" | null | "sqlserver" | null | null | "localhost" | 1433 | "MSSQLSERVER" | null
"jdbc:microsoft:sqlserver://;" | stdProps | "sqlserver" | null | "stdUserName" | "stdServerName" | 9999 | "MSSQLSERVER" | "stdDatabaseName"
"jdbc:sqlserver://ss.host\\ssinstance:44;databaseName=ssdb;user=ssuser;password=pw" | null | "sqlserver" | null | "ssuser" | "ss.host" | 44 | "ssinstance" | "ssdb"
"jdbc:sqlserver://;serverName=ss.host\\ssinstance:44;DatabaseName=;" | null | "sqlserver" | null | null | "ss.host" | 44 | "ssinstance" | null
"jdbc:sqlserver://ss.host;serverName=althost;DatabaseName=ssdb;" | null | "sqlserver" | null | null | "ss.host" | 1433 | "MSSQLSERVER" | "ssdb"
"jdbc:microsoft:sqlserver://ss.host:44;DatabaseName=ssdb;user=ssuser;password=pw;user=ssuser2;" | null | "sqlserver" | null | "ssuser" | "ss.host" | 44 | "MSSQLSERVER" | "ssdb"
// https://docs.oracle.com/cd/B28359_01/java.111/b31224/urls.htm
// https://docs.oracle.com/cd/B28359_01/java.111/b31224/jdbcthin.htm
"jdbc:oracle:thin:orcluser/PW@localhost:55:orclsn" | null | "oracle" | "thin" | "orcluser" | "localhost" | 55 | "orclsn" | null
"jdbc:oracle:thin:orcluser/PW@//orcl.host:55/orclsn" | null | "oracle" | "thin" | "orcluser" | "orcl.host" | 55 | "orclsn" | null
"jdbc:oracle:thin:orcluser/PW@127.0.0.1:orclsn" | null | "oracle" | "thin" | "orcluser" | "127.0.0.1" | 1521 | "orclsn" | null
"jdbc:oracle:thin:orcluser/PW@//orcl.host/orclsn" | null | "oracle" | "thin" | "orcluser" | "orcl.host" | 1521 | "orclsn" | null
"jdbc:oracle:thin:@//orcl.host:55/orclsn" | null | "oracle" | "thin" | null | "orcl.host" | 55 | "orclsn" | null
"jdbc:oracle:thin:@ldap://orcl.host:55/some,cn=OracleContext,dc=com" | null | "oracle" | "thin" | null | "orcl.host" | 55 | "some,cn=oraclecontext,dc=com" | null
"jdbc:oracle:thin:127.0.0.1:orclsn" | null | "oracle" | "thin" | null | "127.0.0.1" | 1521 | "orclsn" | null
"jdbc:oracle:thin:orcl.host:orclsn" | stdProps | "oracle" | "thin" | "stdUserName" | "orcl.host" | 9999 | "orclsn" | "stdDatabaseName"
"jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= 127.0.0.1 )(POR T= 666))" +
"(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclsn)))" | null | "oracle" | "thin" | null | "127.0.0.1" | 1521 | "orclsn" | null
// https://docs.oracle.com/cd/B28359_01/java.111/b31224/instclnt.htm
"jdbc:oracle:drivertype:orcluser/PW@orcl.host:55/orclsn" | null | "oracle" | "drivertype" | "orcluser" | "orcl.host" | 55 | "orclsn" | null
"jdbc:oracle:oci8:@" | null | "oracle" | "oci8" | null | null | 1521 | null | null
"jdbc:oracle:oci8:@" | stdProps | "oracle" | "oci8" | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName"
"jdbc:oracle:oci8:@orclsn" | null | "oracle" | "oci8" | null | null | 1521 | "orclsn" | null
"jdbc:oracle:oci:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)( HOST = orcl.host )" +
"( PORT = 55 ))(CONNECT_DATA=(SERVICE_NAME =orclsn )))" | null | "oracle" | "oci" | null | "orcl.host" | 55 | "orclsn" | null
// https://www.ibm.com/support/knowledgecenter/en/SSEPEK_10.0.0/java/src/tpc/imjcc_tjvjcccn.html
// https://www.ibm.com/support/knowledgecenter/en/SSEPGG_10.5.0/com.ibm.db2.luw.apdv.java.doc/src/tpc/imjcc_r0052342.html
"jdbc:db2://db2.host" | null | "db2" | null | null | "db2.host" | 50000 | null | null
"jdbc:db2://db2.host" | stdProps | "db2" | null | "stdUserName" | "db2.host" | 9999 | null | "stdDatabaseName"
"jdbc:db2://db2.host:77/db2db:user=db2user;password=PW;" | null | "db2" | null | "db2user" | "db2.host" | 77 | "db2db" | null
"jdbc:db2://db2.host:77/db2db:user=db2user;password=PW;" | stdProps | "db2" | null | "db2user" | "db2.host" | 77 | "db2db" | "stdDatabaseName"
"jdbc:as400://ashost:66/asdb:user=asuser;password=PW;" | null | "as400" | null | "asuser" | "ashost" | 66 | "asdb" | null
// https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.03/en-US/ff15928cf5594d78b841fbbe649f04b4.html
"jdbc:sap://sap.host" | null | "sap" | null | null | "sap.host" | null | null | null
"jdbc:sap://sap.host" | stdProps | "sap" | null | "stdUserName" | "sap.host" | 9999 | null | "stdDatabaseName"
"jdbc:sap://sap.host:88/?databaseName=sapdb&user=sapuser&password=PW" | null | "sap" | null | "sapuser" | "sap.host" | 88 | null | "sapdb"
// TODO:
// "jdbc:informix-sqli://infxhost:99/infxdb:INFORMIXSERVER=infxsn;user=infxuser;password=PW" | null | "informix-sqli" | null | "infxuser" | "infxhost" | 99 | "infxdb"| null
// "jdbc:informix-direct://infxdb:999;user=infxuser;password=PW" | null | "informix-direct" | null | "infxuser" | "infxhost" | 999 | "infxdb"| null
// http://www.h2database.com/html/features.html#database_url
"jdbc:h2:mem:" | null | "h2" | "mem" | null | null | null | null | null
"jdbc:h2:mem:" | stdProps | "h2" | "mem" | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName"
"jdbc:h2:mem:h2db" | null | "h2" | "mem" | null | null | null | "h2db" | null
"jdbc:h2:tcp://h2.host:111/path/h2db;user=h2user;password=PW" | null | "h2" | "tcp" | "h2user" | "h2.host" | 111 | "path/h2db" | null
"jdbc:h2:ssl://h2.host:111/path/h2db;user=h2user;password=PW" | null | "h2" | "ssl" | "h2user" | "h2.host" | 111 | "path/h2db" | null
"jdbc:h2:/data/h2file" | null | "h2" | "file" | null | null | null | "/data/h2file" | null
"jdbc:h2:file:~/h2file;USER=h2user;PASSWORD=PW" | null | "h2" | "file" | null | null | null | "~/h2file" | null
"jdbc:h2:file:/data/h2file" | null | "h2" | "file" | null | null | null | "/data/h2file" | null
"jdbc:h2:file:C:/data/h2file" | null | "h2" | "file" | null | null | null | "c:/data/h2file" | null
"jdbc:h2:zip:~/db.zip!/h2zip" | null | "h2" | "zip" | null | null | null | "~/db.zip!/h2zip" | null
// http://hsqldb.org/doc/2.0/guide/dbproperties-chapt.html
"jdbc:hsqldb:hsdb" | null | "hsqldb" | "mem" | "SA" | null | null | "hsdb" | null
"jdbc:hsqldb:hsdb" | stdProps | "hsqldb" | "mem" | "stdUserName" | "stdServerName" | 9999 | "hsdb" | "stdDatabaseName"
"jdbc:hsqldb:mem:hsdb" | null | "hsqldb" | "mem" | "SA" | null | null | "hsdb" | null
"jdbc:hsqldb:file:hsdb" | null | "hsqldb" | "file" | "SA" | null | null | "hsdb" | null
"jdbc:hsqldb:file:/loc/hsdb" | null | "hsqldb" | "file" | "SA" | null | null | "/loc/hsdb" | null
"jdbc:hsqldb:file:C:/hsdb" | null | "hsqldb" | "file" | "SA" | null | null | "c:/hsdb" | null
"jdbc:hsqldb:res:hsdb" | null | "hsqldb" | "res" | "SA" | null | null | "hsdb" | null
"jdbc:hsqldb:res:/cp/hsdb" | null | "hsqldb" | "res" | "SA" | null | null | "/cp/hsdb" | null
"jdbc:hsqldb:hsql://hs.host:333/hsdb" | null | "hsqldb" | "hsql" | "SA" | "hs.host" | 333 | "hsdb" | null
"jdbc:hsqldb:hsqls://hs.host/hsdb" | null | "hsqldb" | "hsqls" | "SA" | "hs.host" | 9001 | "hsdb" | null
"jdbc:hsqldb:http://hs.host" | null | "hsqldb" | "http" | "SA" | "hs.host" | 80 | null | null
"jdbc:hsqldb:http://hs.host:333/hsdb" | null | "hsqldb" | "http" | "SA" | "hs.host" | 333 | "hsdb" | null
"jdbc:hsqldb:https://127.0.0.1/hsdb" | null | "hsqldb" | "https" | "SA" | "127.0.0.1" | 443 | "hsdb" | null
// https://db.apache.org/derby/papers/DerbyClientSpec.html#Connection+URL+Format
// https://db.apache.org/derby/docs/10.8/devguide/cdevdvlp34964.html
"jdbc:derby:derbydb" | null | "derby" | "directory" | "APP" | null | null | "derbydb" | null
"jdbc:derby:derbydb" | stdProps | "derby" | "directory" | "stdUserName" | "stdServerName" | 9999 | "derbydb" | "stdDatabaseName"
"jdbc:derby:derbydb;user=derbyuser;password=pw" | null | "derby" | "directory" | "derbyuser" | null | null | "derbydb" | null
"jdbc:derby:memory:derbydb" | null | "derby" | "memory" | "APP" | null | null | "derbydb" | null
"jdbc:derby:memory:;databaseName=derbydb" | null | "derby" | "memory" | "APP" | null | null | null | "derbydb"
"jdbc:derby:memory:derbydb;databaseName=altdb" | null | "derby" | "memory" | "APP" | null | null | "derbydb" | "altdb"
"jdbc:derby:memory:derbydb;user=derbyuser;password=pw" | null | "derby" | "memory" | "derbyuser" | null | null | "derbydb" | null
"jdbc:derby://derby.host:222/memory:derbydb;create=true" | null | "derby" | "network" | "APP" | "derby.host" | 222 | "derbydb" | null
"jdbc:derby://derby.host/memory:derbydb;create=true;user=derbyuser;password=pw" | null | "derby" | "network" | "derbyuser" | "derby.host" | 1527 | "derbydb" | null
"jdbc:derby://127.0.0.1:1527/memory:derbydb;create=true;user=derbyuser;password=pw" | null | "derby" | "network" | "derbyuser" | "127.0.0.1" | 1527 | "derbydb" | null
"jdbc:derby:directory:derbydb;user=derbyuser;password=pw" | null | "derby" | "directory" | "derbyuser" | null | null | "derbydb" | null
"jdbc:derby:classpath:/some/derbydb;user=derbyuser;password=pw" | null | "derby" | "classpath" | "derbyuser" | null | null | "/some/derbydb" | null
"jdbc:derby:jar:/derbydb;user=derbyuser;password=pw" | null | "derby" | "jar" | "derbyuser" | null | null | "/derbydb" | null
"jdbc:derby:jar:(~/path/to/db.jar)/other/derbydb;user=derbyuser;password=pw" | null | "derby" | "jar" | "derbyuser" | null | null | "(~/path/to/db.jar)/other/derbydb" | null
expected = new DBInfo.Builder().type(type).subtype(subtype).user(user).instance(instance).db(db).host(host).port(port).build()
}
}

View File

@ -4,13 +4,13 @@ import com.zaxxer.hikari.HikariDataSource
import datadog.trace.agent.test.AgentTestRunner import datadog.trace.agent.test.AgentTestRunner
import datadog.trace.api.DDSpanTypes import datadog.trace.api.DDSpanTypes
import io.opentracing.tag.Tags import io.opentracing.tag.Tags
import javax.sql.DataSource
import org.apache.derby.jdbc.EmbeddedDriver import org.apache.derby.jdbc.EmbeddedDriver
import org.h2.Driver import org.h2.Driver
import org.hsqldb.jdbc.JDBCDriver import org.hsqldb.jdbc.JDBCDriver
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Unroll import spock.lang.Unroll
import javax.sql.DataSource
import java.sql.CallableStatement import java.sql.CallableStatement
import java.sql.Connection import java.sql.Connection
import java.sql.PreparedStatement import java.sql.PreparedStatement
@ -26,32 +26,42 @@ class JDBCInstrumentationTest extends AgentTestRunner {
@Shared @Shared
private Map<String, String> jdbcUrls = [ private Map<String, String> jdbcUrls = [
h2 : "jdbc:h2:mem:" + dbName, "h2" : "jdbc:h2:mem:$dbName",
derby : "jdbc:derby:memory:" + dbName, "derby" : "jdbc:derby:memory:$dbName",
hsqldb: "jdbc:hsqldb:mem:" + dbName "hsqldb": "jdbc:hsqldb:mem:$dbName",
] ]
@Shared @Shared
private Map<String, String> jdbcDriverClassNames = [ private Map<String, String> jdbcDriverClassNames = [
h2 : "org.h2.Driver", "h2" : "org.h2.Driver",
derby : "org.apache.derby.jdbc.EmbeddedDriver", "derby" : "org.apache.derby.jdbc.EmbeddedDriver",
hsqldb: "org.hsqldb.jdbc.JDBCDriver" "hsqldb": "org.hsqldb.jdbc.JDBCDriver",
] ]
@Shared @Shared
private Map<String, String> jdbcUserNames = [ private Map<String, String> jdbcUserNames = [
h2 : null, "h2" : null,
derby : "APP", "derby" : "APP",
hsqldb: "SA" "hsqldb": "SA",
] ]
@Shared
private Properties connectionProps = {
def props = new Properties()
// props.put("user", "someUser")
// props.put("password", "somePassword")
props.put("databaseName", "someDb")
props.put("OPEN_NEW", "true") // So H2 doesn't complain about username/password.
return props
}()
// JDBC Connection pool name (i.e. HikariCP) -> Map<dbName, Datasource> // JDBC Connection pool name (i.e. HikariCP) -> Map<dbName, Datasource>
@Shared @Shared
private Map<String, Map<String, DataSource>> cpDatasources = new HashMap<>() private Map<String, Map<String, DataSource>> cpDatasources = new HashMap<>()
def prepareConnectionPoolDatasources() { def prepareConnectionPoolDatasources() {
String[] connectionPoolNames = [ String[] connectionPoolNames = [
"tomcat", "hikari", "c3p0" "tomcat", "hikari", "c3p0",
] ]
connectionPoolNames.each { connectionPoolNames.each {
cpName -> cpName ->
@ -170,7 +180,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
} }
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"component" "java-jdbc-statement" "component" "java-jdbc-statement"
"db.instance" jdbcUrls.get(driver) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -187,6 +197,9 @@ class JDBCInstrumentationTest extends AgentTestRunner {
"h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "SELECT 3" "h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "SELECT 3"
"derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" "derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" "hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
"h2" | new Driver().connect(jdbcUrls.get("h2"), connectionProps) | null | "SELECT 3"
"derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), connectionProps) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), connectionProps) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
"h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3"
"derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1"
"hsqldb" | cpDatasources.get("tomcat").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" "hsqldb" | cpDatasources.get("tomcat").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS"
@ -230,7 +243,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
} }
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"component" "java-jdbc-prepared_statement" "component" "java-jdbc-prepared_statement"
"db.instance" jdbcUrls.get(driver) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -285,7 +298,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
} }
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"component" "java-jdbc-prepared_statement" "component" "java-jdbc-prepared_statement"
"db.instance" jdbcUrls.get(driver) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -340,7 +353,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
} }
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"component" "java-jdbc-prepared_statement" "component" "java-jdbc-prepared_statement"
"db.instance" jdbcUrls.get(driver) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -395,7 +408,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
} }
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"component" "java-jdbc-statement" "component" "java-jdbc-statement"
"db.instance" jdbcUrls.get(driver) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -453,7 +466,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
} }
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"component" "java-jdbc-prepared_statement" "component" "java-jdbc-prepared_statement"
"db.instance" jdbcUrls.get(driver) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -528,7 +541,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
"component" "java-jdbc-statement" "component" "java-jdbc-statement"
} }
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"db.instance" jdbcUrls.get(driver) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -585,7 +598,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
res[i] == 3 res[i] == 3
} }
assertTraces(5) { assertTraces(5) {
trace(0, 2) { trace(0, 1) {
span(0) { span(0) {
operationName "${dbType}.query" operationName "${dbType}.query"
serviceName dbType serviceName dbType
@ -597,24 +610,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
"db.user" "SA" "db.user" "SA"
"component" "java-jdbc-prepared_statement" "component" "java-jdbc-prepared_statement"
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"db.instance" jdbcUrls.get(dbType) "db.instance" dbName.toLowerCase()
"span.origin.type" String
defaultTags()
}
}
span(1) {
operationName "${dbType}.query"
serviceName dbType
resourceName "CALL USER()"
spanType DDSpanTypes.SQL
errored false
childOf(span(0))
tags {
"db.type" "hsqldb"
"db.user" "SA"
"component" "java-jdbc-statement"
"span.kind" Tags.SPAN_KIND_CLIENT
"db.instance" jdbcUrls.get(dbType)
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }
@ -633,7 +629,7 @@ class JDBCInstrumentationTest extends AgentTestRunner {
"db.user" "SA" "db.user" "SA"
"component" "java-jdbc-prepared_statement" "component" "java-jdbc-prepared_statement"
"span.kind" Tags.SPAN_KIND_CLIENT "span.kind" Tags.SPAN_KIND_CLIENT
"db.instance" jdbcUrls.get(dbType) "db.instance" dbName.toLowerCase()
"span.origin.type" String "span.origin.type" String
defaultTags() defaultTags()
} }

View File

@ -4,6 +4,8 @@ import datadog.opentracing.DDSpan
import groovy.transform.stc.ClosureParams import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType import groovy.transform.stc.SimpleType
import java.util.regex.Pattern
import static TagsAssert.assertTags import static TagsAssert.assertTags
class SpanAssert { class SpanAssert {
@ -52,6 +54,11 @@ class SpanAssert {
checked.operationName = true checked.operationName = true
} }
def resourceName(Pattern pattern) {
assert span.resourceName.matches(pattern)
checked.resourceName = true
}
def resourceName(String name) { def resourceName(String name) {
assert span.resourceName == name assert span.resourceName == name
checked.resourceName = true checked.resourceName = true