Merge pull request #866 from DataDog/tyler/jdbc-instance
Attempt to properly parse out instance name from JDBC url
This commit is contained in:
commit
ec3b586c2f
|
@ -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(
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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"
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue