From 6100443443f1e8c8701e3d46415f3ba2634f059a Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 5 Jun 2019 18:01:33 -0700 Subject: [PATCH 01/22] Attempt to properly parse out instance name from JDBC url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately implementations are INCREDIBLY inconsistent on the matter. Oracle implementation is still pending since it’s really complicated. --- .../jdbc/DriverInstrumentation.java | 61 ++ .../jdbc/JDBCConnectionUrlParser.java | 538 ++++++++++++++++++ .../instrumentation/jdbc/JDBCDecorator.java | 20 +- .../trace/instrumentation/jdbc/JDBCMaps.java | 8 +- .../trace/instrumentation/jdbc/JDBCUtils.java | 18 + .../groovy/JDBCConnectionUrlParserTest.groovy | 114 ++++ .../groovy/JDBCInstrumentationTest.groovy | 90 ++- 7 files changed, 787 insertions(+), 62 deletions(-) create mode 100644 dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java create mode 100644 dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java create mode 100644 dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java new file mode 100644 index 0000000000..8cd9d9819e --- /dev/null +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java @@ -0,0 +1,61 @@ +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.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 typeMatcher() { + return not(isInterface()).and(safeHasSuperType(named("java.sql.Driver"))); + } + + @Override + public String[] helperClassNames() { + return new String[] { + packageName + ".JDBCConnectionUrlParser", + }; + } + + @Override + public Map, 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 JDBCMaps.DBInfo dbInfo = JDBCConnectionUrlParser.parse(url, props); + JDBCMaps.connectionInfo.put(connection, dbInfo); + } + } +} diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java new file mode 100644 index 0000000000..7121468d5e --- /dev/null +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java @@ -0,0 +1,538 @@ +package datadog.trace.instrumentation.jdbc; + +import static datadog.trace.instrumentation.jdbc.JDBCMaps.DBInfo.DEFAULT; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URLDecoder; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +/** + * Structured as an enum instead of a class hierarchy to allow iterating through the parsers, plus + * the added benefit of only a single class to inject. + */ +public enum JDBCConnectionUrlParser { + GENERIC_URL_LIKE() { + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + try { + // Attempt generic parsing + final URI uri = new URI(jdbcUrl); + + String username = uri.getUserInfo(); + String databaseName = null; + if (uri.getQuery() != null) { + final Map queryParams = splitQuery(uri.getQuery(), "&"); + + if (username == null) { + username = queryParams.get("user"); + } + databaseName = queryParams.get("databasename"); + } + + String path = uri.getPath(); + if (path.startsWith("/")) { + path = path.substring(1); + } + + return new JDBCMaps.DBInfo( + uri.getScheme(), null, username, path, databaseName, uri.getHost(), uri.getPort()); + } catch (final Exception e) { + return DEFAULT; + } + } + }, + + MODIFIED_URL_LIKE() { + private static final String DEFAULT_HOST = "localhost"; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + try { + + final String type; + String serverName = ""; + Integer port = null; + String databaseName = null; + String instanceName = null; + String username = null; + + final int hostIndex = jdbcUrl.indexOf("://"); + + if (hostIndex <= 0) { + return DEFAULT; + } + + 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 urlProps = splitQuery(split[1], ";"); + if (urlProps.containsKey("servername")) { + serverName = urlProps.get("servername"); + } + if (urlProps.containsKey("instancename")) { + instanceName = urlProps.get("instancename"); + } + if (urlProps.containsKey("databasename")) { + databaseName = urlProps.get("databasename"); + } + if (urlProps.containsKey("user")) { + username = urlProps.get("user"); + } + } + + 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 (serverName.isEmpty()) { + serverName = DEFAULT_HOST; + } + + return new JDBCMaps.DBInfo( + type, null, username, instanceName, null /* databaseName */, serverName, port); + } catch (final UnsupportedEncodingException e) { + return DEFAULT; + } + } + }, + + POSTGRES("postgresql") { + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 5432; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + final JDBCMaps.DBInfo dbInfo = GENERIC_URL_LIKE.doParse(jdbcUrl, props); + final String host = dbInfo.getHost() == null ? DEFAULT_HOST : dbInfo.getHost(); + final int port = dbInfo.getPort() <= 0 ? DEFAULT_PORT : dbInfo.getPort(); + return new JDBCMaps.DBInfo( + dbInfo.getType(), + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getInstance(), + null, + host, + port); + } + }, + + MYSQL("mysql", "mariadb") { + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 3306; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + final JDBCMaps.DBInfo dbInfo = GENERIC_URL_LIKE.doParse(jdbcUrl, props); + final String host = dbInfo.getHost() == null ? DEFAULT_HOST : dbInfo.getHost(); + final int port = dbInfo.getPort() <= 0 ? DEFAULT_PORT : dbInfo.getPort(); + return new JDBCMaps.DBInfo( + dbInfo.getType(), + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getInstance(), + null, + host, + port); + } + }, + + SAP("sap") { + private static final String DEFAULT_HOST = "localhost"; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + final JDBCMaps.DBInfo dbInfo = GENERIC_URL_LIKE.doParse(jdbcUrl, props); + final String host = dbInfo.getHost() == null ? DEFAULT_HOST : dbInfo.getHost(); + return new JDBCMaps.DBInfo( + dbInfo.getType(), + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getDb(), + null, + host, + dbInfo.getPort()); + } + }, + + MSSQLSERVER("microsoft", "sqlserver") { + private static final int DEFAULT_PORT = 1433; + private static final String DEFAULT_INSTANCE = "MSSQLSERVER"; + + @Override + JDBCMaps.DBInfo doParse(String jdbcUrl, final Properties props) { + if (jdbcUrl.startsWith("microsoft:")) { + jdbcUrl = jdbcUrl.substring("microsoft:".length()); + } + if (!jdbcUrl.startsWith("sqlserver://")) { + return DEFAULT; + } + + final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + + return new JDBCMaps.DBInfo( + "sqlserver", + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getInstance() == null ? DEFAULT_INSTANCE : dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + } + }, + + DB2("db2", "as400") { + private static final int DEFAULT_PORT = 50000; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + return dbInfo.getPort() != null + ? dbInfo + : new JDBCMaps.DBInfo( + dbInfo.getType(), + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + DEFAULT_PORT); + } + }, + + H2("h2") { + private static final int DEFAULT_PORT = 8082; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + String type = "h2"; + String instance = null; + + final String h2Url = jdbcUrl.substring("h2:".length()); + if (h2Url.startsWith("mem:")) { + type = "h2: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:")) { + type = "h2: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:")) { + type = "h2: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 JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + + return new JDBCMaps.DBInfo( + "h2:tcp", + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + } else if (h2Url.startsWith("ssl:")) { + final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + + return new JDBCMaps.DBInfo( + "h2:ssl", + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + } else { + type = "h2:file"; + final int propLoc = h2Url.indexOf(";"); + if (propLoc >= 0) { + instance = h2Url.substring(0, propLoc); + } else { + instance = h2Url; + } + } + return new JDBCMaps.DBInfo(type, null, null, instance, null, null, null); + } + }, + + HSQL("hsqldb") { + private static final String DEFAULT_USER = "SA"; + private static final int DEFAULT_PORT = 9001; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + String type = "hsqldb"; + String instance = null; + final String hsqlUrl = jdbcUrl.substring("hsqldb:".length()); + if (hsqlUrl.startsWith("mem:")) { + type = "hsqldb:mem"; + instance = hsqlUrl.substring("mem:".length()); + } else if (hsqlUrl.startsWith("file:")) { + type = "hsqldb:file"; + instance = hsqlUrl.substring("file:".length()); + } else if (hsqlUrl.startsWith("res:")) { + type = "hsqldb:res"; + instance = hsqlUrl.substring("res:".length()); + } else if (hsqlUrl.startsWith("hsql:")) { + final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + + return new JDBCMaps.DBInfo( + "hsqldb:hsql", + dbInfo.getUrl(), + dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + + } else if (hsqlUrl.startsWith("hsqls:")) { + final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + + return new JDBCMaps.DBInfo( + "hsqldb:hsqls", + dbInfo.getUrl(), + dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + + } else if (hsqlUrl.startsWith("http:")) { + final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + + return new JDBCMaps.DBInfo( + "hsqldb:http", + dbInfo.getUrl(), + dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? 80 : dbInfo.getPort()); + + } else if (hsqlUrl.startsWith("https:")) { + final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); + + return new JDBCMaps.DBInfo( + "hsqldb:https", + dbInfo.getUrl(), + dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? 443 : dbInfo.getPort()); + + } else { + type = "hsqldb:mem"; + instance = hsqlUrl; + } + return new JDBCMaps.DBInfo(type, null, DEFAULT_USER, instance, null, null, null); + } + }, + + DERBY("derby") { + private static final String DEFAULT_USER = "APP"; + private static final int DEFAULT_PORT = 1527; + + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + String type = "derby"; + String instance = null; + String host = null; + Integer port = null; + String user = DEFAULT_USER; + + if (props != null) { + instance = props.getProperty("databasename"); + user = props.getProperty("user"); + } + final String derbyUrl = jdbcUrl.substring("derby:".length()); + final String[] split = derbyUrl.split(";", 2); + + if (split.length > 1) { + try { + final Map urlProps = splitQuery(split[1], ";"); + if (urlProps.containsKey("databasename")) { + instance = urlProps.get("databasename"); + } + if (urlProps.containsKey("user")) { + user = urlProps.get("user"); + } + if (urlProps.containsKey("servername")) { + host = urlProps.get("servername"); + } + if (urlProps.containsKey("portnumber")) { + port = Integer.parseInt(urlProps.get("portnumber")); + } + } catch (final Exception e) { + } + } + + if (split[0].startsWith("memory:")) { + type = "derby:memory"; + final String urlInstance = split[0].substring("memory:".length()); + if (!urlInstance.isEmpty()) { + instance = urlInstance; + } + } else if (split[0].startsWith("directory:")) { + type = "derby:directory"; + final String urlInstance = split[0].substring("directory:".length()); + if (!urlInstance.isEmpty()) { + instance = urlInstance; + } + } else if (split[0].startsWith("classpath:")) { + type = "derby:classpath"; + final String urlInstance = split[0].substring("classpath:".length()); + if (!urlInstance.isEmpty()) { + instance = urlInstance; + } + } else if (split[0].startsWith("jar:")) { + type = "derby:jar"; + final String urlInstance = split[0].substring("jar:".length()); + if (!urlInstance.isEmpty()) { + instance = urlInstance; + } + } else if (split[0].startsWith("//")) { + type = "derby:network"; + String url = split[0].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); + port = Integer.parseInt(url.substring(portLoc + 1)); + } else { + host = url; + port = DEFAULT_PORT; + } + } else { + type = "derby:directory"; + final String urlInstance = split[0]; + if (!urlInstance.isEmpty()) { + instance = urlInstance; + } + } + + return new JDBCMaps.DBInfo(type, null, user, instance, null, host, port); + } + }; + + private static final Map 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 JDBCMaps.DBInfo doParse(String jdbcUrl, final Properties props); + + public static JDBCMaps.DBInfo parse(String connectionUrl, final Properties props) { + if (connectionUrl == null) { + return DEFAULT; + } + // Make this easer 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); + + if (typeParsers.containsKey(baseType)) { + // Delegate to specific parser + return typeParsers.get(baseType).doParse(jdbcUrl, props); + } + return GENERIC_URL_LIKE.doParse(connectionUrl, props); + } + + // Source: https://stackoverflow.com/a/13592567 + private static Map splitQuery(final String query, final String separator) + throws UnsupportedEncodingException { + final Map query_pairs = new LinkedHashMap<>(); + final String[] pairs = query.split(separator); + for (final String pair : pairs) { + 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); + } + } + return query_pairs; + } +} diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java index 7de8cca72f..213e18398c 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java @@ -45,16 +45,17 @@ public class JDBCDecorator extends DatabaseClientDecorator { @Override protected String dbInstance(final JDBCMaps.DBInfo info) { - return info.getUrl(); + return info.getInstance(); } public Span onConnection(final Span span, final Connection connection) { JDBCMaps.DBInfo dbInfo = JDBCMaps.connectionInfo.get(connection); /** - * Logic to get the DBInfo from a JDBC Connection, if the connection was never seen before, the - * connectionInfo map will return null and will attempt to extract DBInfo from the connection. - * If the DBInfo can't be extracted, then the connection will be stored with the DEFAULT DBInfo - * as the value in the connectionInfo map to avoid retry overhead. + * Logic to get the DBInfo from a JDBC Connection, if the connection was not created via + * Driver.connect, or it has never seen before, the connectionInfo map will return null and will + * attempt to extract DBInfo from the connection. If the DBInfo can't be extracted, then the + * connection will be stored with the DEFAULT DBInfo as the value in the connectionInfo map to + * avoid retry overhead. */ { if (dbInfo == null) { @@ -62,14 +63,7 @@ public class JDBCDecorator extends DatabaseClientDecorator { final DatabaseMetaData metaData = connection.getMetaData(); final String url = metaData.getURL(); if (url != null) { - // Remove end of url to prevent passwords from leaking: - 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); + dbInfo = JDBCConnectionUrlParser.parse(url, connection.getClientInfo()); } else { dbInfo = JDBCMaps.DBInfo.DEFAULT; } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java index 13bbe8865e..d4b4135125 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java @@ -20,9 +20,13 @@ public class JDBCMaps { @Data public static class DBInfo { - public static DBInfo DEFAULT = new DBInfo("null", "database", null); - private final String url; + public static DBInfo DEFAULT = new DBInfo("database", null, null, null, null, null, null); private final String type; + private final String url; private final String user; + private final String instance; + private final String db; + private final String host; + private final Integer port; } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCUtils.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCUtils.java index 0fb70291f6..a7883679cc 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCUtils.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCUtils.java @@ -1,10 +1,12 @@ package datadog.trace.instrumentation.jdbc; import datadog.trace.bootstrap.ExceptionLogger; +import java.lang.reflect.Field; import java.sql.Connection; import java.sql.Statement; public abstract class JDBCUtils { + private static Field c3poField = null; /** * @param statement @@ -14,6 +16,13 @@ public abstract class JDBCUtils { Connection connection; try { connection = statement.getConnection(); + + if (c3poField != null) { + if (connection.getClass().getName().equals("com.mchange.v2.c3p0.impl.NewProxyConnection")) { + return (Connection) c3poField.get(connection); + } + } + try { // unwrap the connection to cache the underlying actual connection and to not cache proxy // objects @@ -21,6 +30,15 @@ public abstract class JDBCUtils { connection = connection.unwrap(Connection.class); } } catch (final Exception | AbstractMethodError e) { + // Attempt to work around c3po delegating to an connection that doesn't support unwrapping. + final Class 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? // ex: org.h2.jdbc.JdbcConnection v1.3.175 // or: jdts.jdbc which always throws `AbstractMethodError` (at least up to version 1.3) diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy new file mode 100644 index 0000000000..3a4a969d98 --- /dev/null +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy @@ -0,0 +1,114 @@ +import datadog.trace.instrumentation.jdbc.JDBCMaps +import spock.lang.Specification + +import static datadog.trace.instrumentation.jdbc.JDBCConnectionUrlParser.parse + +class JDBCConnectionUrlParserTest extends Specification { + + def "invalid url returns default"() { + expect: + parse(url, null) == JDBCMaps.DBInfo.DEFAULT + + where: + url | _ + null | _ + "" | _ + "bogus:string" | _ + } + + def "verify #format parsing of #url"() { + setup: + def info = parse(url, null) + + 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 | format | user | host | port | instance + // https://jdbc.postgresql.org/documentation/94/connect.html + "jdbc:postgresql:///" | "postgresql" | null | "localhost" | 5432 | "" + "jdbc:postgresql://pghost" | "postgresql" | null | "pghost" | 5432 | "" + "jdbc:postgresql://pghost:11/pgdb?user=pguser&password=PW" | "postgresql" | "pguser" | "pghost" | 11 | "pgdb" + + // https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html + "jdbc:mysql:///" | "mysql" | null | "localhost" | 3306 | "" + "jdbc:mysql://myhost" | "mysql" | null | "myhost" | 3306 | "" + "jdbc:mysql://myhost?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 3306 | "" + "jdbc:mysql://myhost:22/mydb?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 22 | "mydb" + "jdbc:mariadb://mdbhost:33/mdbdb?user=mdbuser&password=PW" | "mariadb" | "mdbuser" | "mdbhost" | 33 | "mdbdb" + + //https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url + "jdbc:microsoft:sqlserver://;" | "sqlserver" | null | "localhost" | 1433 | "MSSQLSERVER" + "jdbc:sqlserver://sshost\\ssinstance:44;databaseName=ssdb;user=ssuser;password=pw" | "sqlserver" | "ssuser" | "sshost" | 44 | "ssinstance" + "jdbc:sqlserver://;serverName=sshost\\ssinstance:44;DatabaseName=;" | "sqlserver" | null | "sshost" | 44 | "ssinstance" + "jdbc:sqlserver://sshost;serverName=althost;DatabaseName=ssdb;" | "sqlserver" | null | "sshost" | 1433 | "MSSQLSERVER" + "jdbc:microsoft:sqlserver://sshost:44;DatabaseName=ssdb;user=ssuser;password=pw;user=ssuser2;" | "sqlserver" | "ssuser" | "sshost" | 44 | "MSSQLSERVER" + + // TODO: +// "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= localhost )(PORT= 1521))" + +// "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))" | "oracle" | "orcluser" | "orclhost" | 55 | "orcldb" +// "jdbc:oracle:drivertype:orcluser/PW@orclhost:55/orcldb" | "oracle" | "orcluser" | "orclhost" | 55 | "orcldb" + + // 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:as400://ashost:66/asdb:user=asuser;password=PW;" | "as400" | "asuser" | "ashost" | 66 | "asdb" + "jdbc:db2://db2host:77/db2db:user=db2user;password=PW;" | "db2" | "db2user" | "db2host" | 77 | "db2db" + + // https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.03/en-US/ff15928cf5594d78b841fbbe649f04b4.html + "jdbc:sap://saphost:88/?databaseName=sapdb&user=sapuser&password=PW" | "sap" | "sapuser" | "saphost" | 88 | "sapdb" + + // TODO: +// "jdbc:informix-sqli://infxhost:99/infxdb:INFORMIXSERVER=infxsn;user=infxuser;password=PW" | "informix-sqli" | "infxuser" | "infxhost" | 99 | "infxdb" +// "jdbc:informix-direct://infxdb:999;user=infxuser;password=PW" | "informix-direct" | "infxuser" | "infxhost" | 999 | "infxdb" + + // http://www.h2database.com/html/features.html#database_url + "jdbc:h2:mem:" | "h2:mem" | null | null | null | "" + "jdbc:h2:mem:h2db" | "h2:mem" | null | null | null | "h2db" + "jdbc:h2:tcp://h2host:111/path/h2db;user=h2user;password=PW" | "h2:tcp" | "h2user" | "h2host" | 111 | "path/h2db" + "jdbc:h2:ssl://h2host:111/path/h2db;user=h2user;password=PW" | "h2:ssl" | "h2user" | "h2host" | 111 | "path/h2db" + "jdbc:h2:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" + "jdbc:h2:file:~/h2file;USER=h2user;PASSWORD=PW" | "h2:file" | null | null | null | "~/h2file" + "jdbc:h2:file:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" + "jdbc:h2:file:C:/data/h2file" | "h2:file" | null | null | null | "c:/data/h2file" + "jdbc:h2:zip:~/db.zip!/h2zip" | "h2:zip" | null | null | null | "~/db.zip!/h2zip" + + // http://hsqldb.org/doc/2.0/guide/dbproperties-chapt.html + "jdbc:hsqldb:hsdb" | "hsqldb:mem" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:mem:hsdb" | "hsqldb:mem" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:file:hsdb" | "hsqldb:file" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:file:/loc/hsdb" | "hsqldb:file" | "SA" | null | null | "/loc/hsdb" + "jdbc:hsqldb:file:C:/hsdb" | "hsqldb:file" | "SA" | null | null | "c:/hsdb" + "jdbc:hsqldb:res:hsdb" | "hsqldb:res" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:res:/cp/hsdb" | "hsqldb:res" | "SA" | null | null | "/cp/hsdb" + "jdbc:hsqldb:hsql://hshost:333/hsdb" | "hsqldb:hsql" | "SA" | "hshost" | 333 | "hsdb" + "jdbc:hsqldb:hsqls://hshost/hsdb" | "hsqldb:hsqls" | "SA" | "hshost" | 9001 | "hsdb" + "jdbc:hsqldb:http://hshost" | "hsqldb:http" | "SA" | "hshost" | 80 | null + "jdbc:hsqldb:http://hshost:333/hsdb" | "hsqldb:http" | "SA" | "hshost" | 333 | "hsdb" + "jdbc:hsqldb:https://127.0.0.1/hsdb" | "hsqldb:https" | "SA" | "127.0.0.1" | 443 | "hsdb" + + // 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" | "derby:directory" | "APP" | null | null | "derbydb" + "jdbc:derby:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" + "jdbc:derby:memory:derbydb" | "derby:memory" | "APP" | null | null | "derbydb" + "jdbc:derby:memory:;databaseName=derbydb" | "derby:memory" | "APP" | null | null | "derbydb" + "jdbc:derby:memory:derbydb;databaseName=altdb" | "derby:memory" | "APP" | null | null | "derbydb" + "jdbc:derby:memory:derbydb;user=derbyuser;password=pw" | "derby:memory" | "derbyuser" | null | null | "derbydb" + "jdbc:derby://derbyhost:222/memory:derbydb;create=true" | "derby:network" | "APP" | "derbyhost" | 222 | "derbydb" + "jdbc:derby://derbyhost/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "derbyhost" | 1527 | "derbydb" + "jdbc:derby://127.0.0.1:1527/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "127.0.0.1" | 1527 | "derbydb" + "jdbc:derby:directory:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" + "jdbc:derby:classpath:/some/derbydb;user=derbyuser;password=pw" | "derby:classpath" | "derbyuser" | null | null | "/some/derbydb" + "jdbc:derby:jar:/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "/derbydb" + "jdbc:derby:jar:(~/path/to/db.jar)/other/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "(~/path/to/db.jar)/other/derbydb" + + expected = new JDBCMaps.DBInfo(format, null, user, instance, null, host, port) + } +} diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy index cc5a0e1ee5..c903b7fc33 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy @@ -4,13 +4,13 @@ import com.zaxxer.hikari.HikariDataSource import datadog.trace.agent.test.AgentTestRunner import datadog.trace.api.DDSpanTypes import io.opentracing.tag.Tags +import javax.sql.DataSource import org.apache.derby.jdbc.EmbeddedDriver import org.h2.Driver import org.hsqldb.jdbc.JDBCDriver import spock.lang.Shared import spock.lang.Unroll -import javax.sql.DataSource import java.sql.CallableStatement import java.sql.Connection import java.sql.PreparedStatement @@ -26,32 +26,42 @@ class JDBCInstrumentationTest extends AgentTestRunner { @Shared private Map jdbcUrls = [ - h2 : "jdbc:h2:mem:" + dbName, - derby : "jdbc:derby:memory:" + dbName, - hsqldb: "jdbc:hsqldb:mem:" + dbName + h2 : "jdbc:h2:mem:$dbName", + derby : "jdbc:derby:memory:$dbName", + hsqldb: "jdbc:hsqldb:mem:$dbName", ] @Shared private Map jdbcDriverClassNames = [ h2 : "org.h2.Driver", derby : "org.apache.derby.jdbc.EmbeddedDriver", - hsqldb: "org.hsqldb.jdbc.JDBCDriver" + hsqldb: "org.hsqldb.jdbc.JDBCDriver", ] @Shared private Map jdbcUserNames = [ h2 : null, 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 @Shared private Map> cpDatasources = new HashMap<>() def prepareConnectionPoolDatasources() { String[] connectionPoolNames = [ - "tomcat", "hikari", "c3p0" + "tomcat", "hikari", "c3p0", ] connectionPoolNames.each { cpName -> @@ -170,7 +180,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { } "span.kind" Tags.SPAN_KIND_CLIENT "component" "java-jdbc-statement" - "db.instance" jdbcUrls.get(driver) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -183,19 +193,22 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "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" - "hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" - "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" - "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" - "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" - "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + driver | connection | username | query + "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" + "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" + "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" + "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" } @Unroll @@ -230,7 +243,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { } "span.kind" Tags.SPAN_KIND_CLIENT "component" "java-jdbc-prepared_statement" - "db.instance" jdbcUrls.get(driver) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -285,7 +298,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { } "span.kind" Tags.SPAN_KIND_CLIENT "component" "java-jdbc-prepared_statement" - "db.instance" jdbcUrls.get(driver) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -340,7 +353,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { } "span.kind" Tags.SPAN_KIND_CLIENT "component" "java-jdbc-prepared_statement" - "db.instance" jdbcUrls.get(driver) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -395,7 +408,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { } "span.kind" Tags.SPAN_KIND_CLIENT "component" "java-jdbc-statement" - "db.instance" jdbcUrls.get(driver) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -453,7 +466,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { } "span.kind" Tags.SPAN_KIND_CLIENT "component" "java-jdbc-prepared_statement" - "db.instance" jdbcUrls.get(driver) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -528,7 +541,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { "component" "java-jdbc-statement" } "span.kind" Tags.SPAN_KIND_CLIENT - "db.instance" jdbcUrls.get(driver) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -585,7 +598,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { res[i] == 3 } assertTraces(5) { - trace(0, 2) { + trace(0, 1) { span(0) { operationName "${dbType}.query" serviceName dbType @@ -597,24 +610,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { "db.user" "SA" "component" "java-jdbc-prepared_statement" "span.kind" Tags.SPAN_KIND_CLIENT - "db.instance" jdbcUrls.get(dbType) - "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) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } @@ -633,7 +629,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { "db.user" "SA" "component" "java-jdbc-prepared_statement" "span.kind" Tags.SPAN_KIND_CLIENT - "db.instance" jdbcUrls.get(dbType) + "db.instance" dbName.toLowerCase() "span.origin.type" String defaultTags() } From ec60d679d6be0f0dbc49edfaa3737ac675155e5a Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 6 Jun 2019 18:03:56 -0700 Subject: [PATCH 02/22] Add Oracle support and fix muzzle. --- .../tooling/muzzle/ReferenceCreator.java | 10 +- .../jdbc/DriverInstrumentation.java | 16 +- .../jdbc/JDBCConnectionUrlParser.java | 200 ++++++++++++++++-- .../PreparedStatementInstrumentation.java | 28 ++- .../jdbc/StatementInstrumentation.java | 28 ++- .../groovy/JDBCConnectionUrlParserTest.groovy | 123 ++++++----- .../groovy/JDBCInstrumentationTest.groovy | 166 +++++++-------- 7 files changed, 397 insertions(+), 174 deletions(-) diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java index 52289b56ff..6899cfeaba 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceCreator.java @@ -260,7 +260,10 @@ public class ReferenceCreator extends ClassVisitor { // * DONE field-source class (descriptor) // * 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 List 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 methodFlags = new ArrayList<>(); methodFlags.add( diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java index 8cd9d9819e..7512065d5b 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java @@ -12,6 +12,8 @@ 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; @@ -33,9 +35,17 @@ public final class DriverInstrumentation extends Instrumenter.Default { @Override public String[] helperClassNames() { - return new String[] { - packageName + ".JDBCConnectionUrlParser", - }; + final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); + final List parserClasses = new ArrayList<>(parsers.length + 3); + + parserClasses.add(packageName + ".JDBCMaps"); + parserClasses.add(packageName + ".JDBCMaps$DBInfo"); + parserClasses.add(packageName + ".JDBCConnectionUrlParser"); + + for (final JDBCConnectionUrlParser parser : parsers) { + parserClasses.add(parser.getClass().getName()); + } + return parserClasses.toArray(new String[0]); } @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java index 7121468d5e..8cf475eb49 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java @@ -2,6 +2,7 @@ package datadog.trace.instrumentation.jdbc; import static datadog.trace.instrumentation.jdbc.JDBCMaps.DBInfo.DEFAULT; +import datadog.trace.bootstrap.ExceptionLogger; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; @@ -9,6 +10,8 @@ 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, plus @@ -22,13 +25,13 @@ public enum JDBCConnectionUrlParser { // Attempt generic parsing final URI uri = new URI(jdbcUrl); - String username = uri.getUserInfo(); + String user = uri.getUserInfo(); String databaseName = null; if (uri.getQuery() != null) { final Map queryParams = splitQuery(uri.getQuery(), "&"); - if (username == null) { - username = queryParams.get("user"); + if (user == null) { + user = queryParams.get("user"); } databaseName = queryParams.get("databasename"); } @@ -39,7 +42,7 @@ public enum JDBCConnectionUrlParser { } return new JDBCMaps.DBInfo( - uri.getScheme(), null, username, path, databaseName, uri.getHost(), uri.getPort()); + uri.getScheme(), null, user, path, databaseName, uri.getHost(), uri.getPort()); } catch (final Exception e) { return DEFAULT; } @@ -58,7 +61,7 @@ public enum JDBCConnectionUrlParser { Integer port = null; String databaseName = null; String instanceName = null; - String username = null; + String user = null; final int hostIndex = jdbcUrl.indexOf("://"); @@ -92,7 +95,7 @@ public enum JDBCConnectionUrlParser { databaseName = urlProps.get("databasename"); } if (urlProps.containsKey("user")) { - username = urlProps.get("user"); + user = urlProps.get("user"); } } @@ -125,7 +128,7 @@ public enum JDBCConnectionUrlParser { } return new JDBCMaps.DBInfo( - type, null, username, instanceName, null /* databaseName */, serverName, port); + type, null, user, instanceName, null /* databaseName */, serverName, port); } catch (final UnsupportedEncodingException e) { return DEFAULT; } @@ -235,6 +238,172 @@ public enum JDBCConnectionUrlParser { } }, + ORACLE("oracle") { + private static final int DEFAULT_PORT = 1521; + + @Override + JDBCMaps.DBInfo doParse(String jdbcUrl, final Properties props) { + final int typeEndIndex = jdbcUrl.indexOf(":", "oracle:".length()); + final String type = jdbcUrl.substring(0, typeEndIndex); + jdbcUrl = jdbcUrl.substring(typeEndIndex + 1); + + final JDBCMaps.DBInfo dbInfo; + if (jdbcUrl.contains("@")) { + dbInfo = ORACLE_AT.doParse(jdbcUrl, props); + } else { + dbInfo = ORACLE_CONNECT_INFO.doParse(jdbcUrl, props); + } + return new JDBCMaps.DBInfo( + type, + dbInfo.getUrl(), + dbInfo.getUser(), + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + } + }, + + ORACLE_CONNECT_INFO() { + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + + 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 DEFAULT; + } else { + host = null; + port = null; + instance = jdbcUrl; + } + } + } + return new JDBCMaps.DBInfo(null, null, null, instance, null, host, port); + } + }, + + ORACLE_AT() { + @Override + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + if (jdbcUrl.contains("@(description")) { + return ORACLE_AT_DESCRIPTION.doParse(jdbcUrl, props); + } + 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; + } + final JDBCMaps.DBInfo dbInfo = + ORACLE_CONNECT_INFO.doParse(connectInfo.substring(hostStart), props); + + return new JDBCMaps.DBInfo( + null, + dbInfo.getUrl(), + user, + dbInfo.getInstance(), + dbInfo.getDb(), + dbInfo.getHost(), + dbInfo.getPort()); + } + }, + + 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 + JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + final String user; + final String host; + final Integer port; + final String instance; + + 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 description = atSplit[1]; + final Matcher hostMatcher = HOST_REGEX.matcher(description); + final Matcher portMatcher = PORT_REGEX.matcher(description); + final Matcher instanceMatcher = INSTANCE_REGEX.matcher(description); + if (hostMatcher.find()) { + host = hostMatcher.group(1); + } else { + host = null; + } + if (portMatcher.find()) { + port = Integer.parseInt(portMatcher.group(1)); + } else { + port = null; + } + if (instanceMatcher.find()) { + instance = instanceMatcher.group(1); + } else { + instance = null; + } + return new JDBCMaps.DBInfo(null, null, user, instance, null, host, port); + } + }, + H2("h2") { private static final int DEFAULT_PORT = 8082; @@ -390,8 +559,8 @@ public enum JDBCConnectionUrlParser { String user = DEFAULT_USER; if (props != null) { - instance = props.getProperty("databasename"); - user = props.getProperty("user"); + instance = props.getProperty("databasename", instance); + user = props.getProperty("user", user); } final String derbyUrl = jdbcUrl.substring("derby:".length()); final String[] split = derbyUrl.split(";", 2); @@ -510,11 +679,16 @@ public enum JDBCConnectionUrlParser { final String baseType = jdbcUrl.substring(0, typeLoc); - if (typeParsers.containsKey(baseType)) { - // Delegate to specific parser - return typeParsers.get(baseType).doParse(jdbcUrl, props); + try { + if (typeParsers.containsKey(baseType)) { + // Delegate to specific parser + return typeParsers.get(baseType).doParse(jdbcUrl, props); + } + return GENERIC_URL_LIKE.doParse(connectionUrl, props); + } catch (final Exception e) { + ExceptionLogger.LOGGER.debug("Error parsing URL", e); + return DEFAULT; } - return GENERIC_URL_LIKE.doParse(connectionUrl, props); } // Source: https://stackoverflow.com/a/13592567 diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java index 56e73233d1..e457cb2000 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java @@ -20,6 +20,8 @@ import io.opentracing.noop.NoopScopeManager; import io.opentracing.util.GlobalTracer; import java.sql.Connection; import java.sql.PreparedStatement; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -40,15 +42,23 @@ public final class PreparedStatementInstrumentation extends Instrumenter.Default @Override public String[] helperClassNames() { - return new String[] { - "datadog.trace.agent.decorator.BaseDecorator", - "datadog.trace.agent.decorator.ClientDecorator", - "datadog.trace.agent.decorator.DatabaseClientDecorator", - packageName + ".JDBCDecorator", - packageName + ".JDBCMaps", - packageName + ".JDBCMaps$DBInfo", - packageName + ".JDBCUtils", - }; + final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); + final List parserClasses = new ArrayList<>(parsers.length + 8); + + parserClasses.add(packageName + ".JDBCUtils"); + parserClasses.add(packageName + ".JDBCMaps"); + parserClasses.add(packageName + ".JDBCMaps$DBInfo"); + parserClasses.add(packageName + ".JDBCConnectionUrlParser"); + + parserClasses.add("datadog.trace.agent.decorator.BaseDecorator"); + parserClasses.add("datadog.trace.agent.decorator.ClientDecorator"); + parserClasses.add("datadog.trace.agent.decorator.DatabaseClientDecorator"); + parserClasses.add(packageName + ".JDBCDecorator"); + + for (final JDBCConnectionUrlParser parser : parsers) { + parserClasses.add(parser.getClass().getName()); + } + return parserClasses.toArray(new String[0]); } @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java index a39e574f8d..a890fddece 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java @@ -20,6 +20,8 @@ import io.opentracing.noop.NoopScopeManager; import io.opentracing.util.GlobalTracer; import java.sql.Connection; import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.method.MethodDescription; @@ -40,15 +42,23 @@ public final class StatementInstrumentation extends Instrumenter.Default { @Override public String[] helperClassNames() { - return new String[] { - "datadog.trace.agent.decorator.BaseDecorator", - "datadog.trace.agent.decorator.ClientDecorator", - "datadog.trace.agent.decorator.DatabaseClientDecorator", - packageName + ".JDBCDecorator", - packageName + ".JDBCMaps", - packageName + ".JDBCMaps$DBInfo", - packageName + ".JDBCUtils", - }; + final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); + final List parserClasses = new ArrayList<>(parsers.length + 8); + + parserClasses.add(packageName + ".JDBCUtils"); + parserClasses.add(packageName + ".JDBCMaps"); + parserClasses.add(packageName + ".JDBCMaps$DBInfo"); + parserClasses.add(packageName + ".JDBCConnectionUrlParser"); + + parserClasses.add("datadog.trace.agent.decorator.BaseDecorator"); + parserClasses.add("datadog.trace.agent.decorator.ClientDecorator"); + parserClasses.add("datadog.trace.agent.decorator.DatabaseClientDecorator"); + parserClasses.add(packageName + ".JDBCDecorator"); + + for (final JDBCConnectionUrlParser parser : parsers) { + parserClasses.add(parser.getClass().getName()); + } + return parserClasses.toArray(new String[0]); } @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy index 3a4a969d98..8707645ebe 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy @@ -31,83 +31,96 @@ class JDBCConnectionUrlParserTest extends Specification { info == expected where: - url | format | user | host | port | instance + url | format | user | host | port | instance // https://jdbc.postgresql.org/documentation/94/connect.html - "jdbc:postgresql:///" | "postgresql" | null | "localhost" | 5432 | "" - "jdbc:postgresql://pghost" | "postgresql" | null | "pghost" | 5432 | "" - "jdbc:postgresql://pghost:11/pgdb?user=pguser&password=PW" | "postgresql" | "pguser" | "pghost" | 11 | "pgdb" + "jdbc:postgresql:///" | "postgresql" | null | "localhost" | 5432 | "" + "jdbc:postgresql://pghost" | "postgresql" | null | "pghost" | 5432 | "" + "jdbc:postgresql://pghost:11/pgdb?user=pguser&password=PW" | "postgresql" | "pguser" | "pghost" | 11 | "pgdb" // https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-configuration-properties.html - "jdbc:mysql:///" | "mysql" | null | "localhost" | 3306 | "" - "jdbc:mysql://myhost" | "mysql" | null | "myhost" | 3306 | "" - "jdbc:mysql://myhost?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 3306 | "" - "jdbc:mysql://myhost:22/mydb?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 22 | "mydb" - "jdbc:mariadb://mdbhost:33/mdbdb?user=mdbuser&password=PW" | "mariadb" | "mdbuser" | "mdbhost" | 33 | "mdbdb" + "jdbc:mysql:///" | "mysql" | null | "localhost" | 3306 | "" + "jdbc:mysql://myhost" | "mysql" | null | "myhost" | 3306 | "" + "jdbc:mysql://myhost?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 3306 | "" + "jdbc:mysql://myhost:22/mydb?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 22 | "mydb" + "jdbc:mariadb://mdbhost:33/mdbdb?user=mdbuser&password=PW" | "mariadb" | "mdbuser" | "mdbhost" | 33 | "mdbdb" //https://docs.microsoft.com/en-us/sql/connect/jdbc/building-the-connection-url - "jdbc:microsoft:sqlserver://;" | "sqlserver" | null | "localhost" | 1433 | "MSSQLSERVER" - "jdbc:sqlserver://sshost\\ssinstance:44;databaseName=ssdb;user=ssuser;password=pw" | "sqlserver" | "ssuser" | "sshost" | 44 | "ssinstance" - "jdbc:sqlserver://;serverName=sshost\\ssinstance:44;DatabaseName=;" | "sqlserver" | null | "sshost" | 44 | "ssinstance" - "jdbc:sqlserver://sshost;serverName=althost;DatabaseName=ssdb;" | "sqlserver" | null | "sshost" | 1433 | "MSSQLSERVER" - "jdbc:microsoft:sqlserver://sshost:44;DatabaseName=ssdb;user=ssuser;password=pw;user=ssuser2;" | "sqlserver" | "ssuser" | "sshost" | 44 | "MSSQLSERVER" + "jdbc:microsoft:sqlserver://;" | "sqlserver" | null | "localhost" | 1433 | "MSSQLSERVER" + "jdbc:sqlserver://sshost\\ssinstance:44;databaseName=ssdb;user=ssuser;password=pw" | "sqlserver" | "ssuser" | "sshost" | 44 | "ssinstance" + "jdbc:sqlserver://;serverName=sshost\\ssinstance:44;DatabaseName=;" | "sqlserver" | null | "sshost" | 44 | "ssinstance" + "jdbc:sqlserver://sshost;serverName=althost;DatabaseName=ssdb;" | "sqlserver" | null | "sshost" | 1433 | "MSSQLSERVER" + "jdbc:microsoft:sqlserver://sshost:44;DatabaseName=ssdb;user=ssuser;password=pw;user=ssuser2;" | "sqlserver" | "ssuser" | "sshost" | 44 | "MSSQLSERVER" - // TODO: -// "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= localhost )(PORT= 1521))" + -// "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)))" | "oracle" | "orcluser" | "orclhost" | 55 | "orcldb" -// "jdbc:oracle:drivertype:orcluser/PW@orclhost:55/orcldb" | "oracle" | "orcluser" | "orclhost" | 55 | "orcldb" + // 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" | "oracle:thin" | "orcluser" | "localhost" | 55 | "orclsn" + "jdbc:oracle:thin:orcluser/PW@//orclhost:55/orclsn" | "oracle:thin" | "orcluser" | "orclhost" | 55 | "orclsn" + "jdbc:oracle:thin:orcluser/PW@127.0.0.1:orclsn" | "oracle:thin" | "orcluser" | "127.0.0.1" | 1521 | "orclsn" + "jdbc:oracle:thin:orcluser/PW@//orclhost/orclsn" | "oracle:thin" | "orcluser" | "orclhost" | 1521 | "orclsn" + "jdbc:oracle:thin:@//orclhost:55/orclsn" | "oracle:thin" | null | "orclhost" | 55 | "orclsn" + "jdbc:oracle:thin:@ldap://orclhost:55/some,cn=OracleContext,dc=com" | "oracle:thin" | null | "orclhost" | 55 | "some,cn=oraclecontext,dc=com" + "jdbc:oracle:thin:127.0.0.1:orclsn" | "oracle:thin" | null | "127.0.0.1" | 1521 | "orclsn" + "jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST= 127.0.0.1 )(POR T= 666))" + + "(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orclsn)))" | "oracle:thin" | null | "127.0.0.1" | 1521 | "orclsn" + // https://docs.oracle.com/cd/B28359_01/java.111/b31224/instclnt.htm + "jdbc:oracle:drivertype:orcluser/PW@orclhost:55/orclsn" | "oracle:drivertype" | "orcluser" | "orclhost" | 55 | "orclsn" + "jdbc:oracle:oci8:@" | "oracle:oci8" | null | null | 1521 | null + "jdbc:oracle:oci8:@orclsn" | "oracle:oci8" | null | null | 1521 | "orclsn" + "jdbc:oracle:oci:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)( HOST = orclhost )" + + "( PORT = 55 ))(CONNECT_DATA=(SERVICE_NAME =orclsn )))" | "oracle:oci" | null | "orclhost" | 55 | "orclsn" // 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:as400://ashost:66/asdb:user=asuser;password=PW;" | "as400" | "asuser" | "ashost" | 66 | "asdb" - "jdbc:db2://db2host:77/db2db:user=db2user;password=PW;" | "db2" | "db2user" | "db2host" | 77 | "db2db" + "jdbc:as400://ashost:66/asdb:user=asuser;password=PW;" | "as400" | "asuser" | "ashost" | 66 | "asdb" + "jdbc:db2://db2host:77/db2db:user=db2user;password=PW;" | "db2" | "db2user" | "db2host" | 77 | "db2db" // https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.03/en-US/ff15928cf5594d78b841fbbe649f04b4.html - "jdbc:sap://saphost:88/?databaseName=sapdb&user=sapuser&password=PW" | "sap" | "sapuser" | "saphost" | 88 | "sapdb" + "jdbc:sap://saphost:88/?databaseName=sapdb&user=sapuser&password=PW" | "sap" | "sapuser" | "saphost" | 88 | "sapdb" // TODO: // "jdbc:informix-sqli://infxhost:99/infxdb:INFORMIXSERVER=infxsn;user=infxuser;password=PW" | "informix-sqli" | "infxuser" | "infxhost" | 99 | "infxdb" // "jdbc:informix-direct://infxdb:999;user=infxuser;password=PW" | "informix-direct" | "infxuser" | "infxhost" | 999 | "infxdb" // http://www.h2database.com/html/features.html#database_url - "jdbc:h2:mem:" | "h2:mem" | null | null | null | "" - "jdbc:h2:mem:h2db" | "h2:mem" | null | null | null | "h2db" - "jdbc:h2:tcp://h2host:111/path/h2db;user=h2user;password=PW" | "h2:tcp" | "h2user" | "h2host" | 111 | "path/h2db" - "jdbc:h2:ssl://h2host:111/path/h2db;user=h2user;password=PW" | "h2:ssl" | "h2user" | "h2host" | 111 | "path/h2db" - "jdbc:h2:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" - "jdbc:h2:file:~/h2file;USER=h2user;PASSWORD=PW" | "h2:file" | null | null | null | "~/h2file" - "jdbc:h2:file:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" - "jdbc:h2:file:C:/data/h2file" | "h2:file" | null | null | null | "c:/data/h2file" - "jdbc:h2:zip:~/db.zip!/h2zip" | "h2:zip" | null | null | null | "~/db.zip!/h2zip" + "jdbc:h2:mem:" | "h2:mem" | null | null | null | "" + "jdbc:h2:mem:h2db" | "h2:mem" | null | null | null | "h2db" + "jdbc:h2:tcp://h2host:111/path/h2db;user=h2user;password=PW" | "h2:tcp" | "h2user" | "h2host" | 111 | "path/h2db" + "jdbc:h2:ssl://h2host:111/path/h2db;user=h2user;password=PW" | "h2:ssl" | "h2user" | "h2host" | 111 | "path/h2db" + "jdbc:h2:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" + "jdbc:h2:file:~/h2file;USER=h2user;PASSWORD=PW" | "h2:file" | null | null | null | "~/h2file" + "jdbc:h2:file:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" + "jdbc:h2:file:C:/data/h2file" | "h2:file" | null | null | null | "c:/data/h2file" + "jdbc:h2:zip:~/db.zip!/h2zip" | "h2:zip" | null | null | null | "~/db.zip!/h2zip" // http://hsqldb.org/doc/2.0/guide/dbproperties-chapt.html - "jdbc:hsqldb:hsdb" | "hsqldb:mem" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:mem:hsdb" | "hsqldb:mem" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:file:hsdb" | "hsqldb:file" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:file:/loc/hsdb" | "hsqldb:file" | "SA" | null | null | "/loc/hsdb" - "jdbc:hsqldb:file:C:/hsdb" | "hsqldb:file" | "SA" | null | null | "c:/hsdb" - "jdbc:hsqldb:res:hsdb" | "hsqldb:res" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:res:/cp/hsdb" | "hsqldb:res" | "SA" | null | null | "/cp/hsdb" - "jdbc:hsqldb:hsql://hshost:333/hsdb" | "hsqldb:hsql" | "SA" | "hshost" | 333 | "hsdb" - "jdbc:hsqldb:hsqls://hshost/hsdb" | "hsqldb:hsqls" | "SA" | "hshost" | 9001 | "hsdb" - "jdbc:hsqldb:http://hshost" | "hsqldb:http" | "SA" | "hshost" | 80 | null - "jdbc:hsqldb:http://hshost:333/hsdb" | "hsqldb:http" | "SA" | "hshost" | 333 | "hsdb" - "jdbc:hsqldb:https://127.0.0.1/hsdb" | "hsqldb:https" | "SA" | "127.0.0.1" | 443 | "hsdb" + "jdbc:hsqldb:hsdb" | "hsqldb:mem" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:mem:hsdb" | "hsqldb:mem" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:file:hsdb" | "hsqldb:file" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:file:/loc/hsdb" | "hsqldb:file" | "SA" | null | null | "/loc/hsdb" + "jdbc:hsqldb:file:C:/hsdb" | "hsqldb:file" | "SA" | null | null | "c:/hsdb" + "jdbc:hsqldb:res:hsdb" | "hsqldb:res" | "SA" | null | null | "hsdb" + "jdbc:hsqldb:res:/cp/hsdb" | "hsqldb:res" | "SA" | null | null | "/cp/hsdb" + "jdbc:hsqldb:hsql://hshost:333/hsdb" | "hsqldb:hsql" | "SA" | "hshost" | 333 | "hsdb" + "jdbc:hsqldb:hsqls://hshost/hsdb" | "hsqldb:hsqls" | "SA" | "hshost" | 9001 | "hsdb" + "jdbc:hsqldb:http://hshost" | "hsqldb:http" | "SA" | "hshost" | 80 | null + "jdbc:hsqldb:http://hshost:333/hsdb" | "hsqldb:http" | "SA" | "hshost" | 333 | "hsdb" + "jdbc:hsqldb:https://127.0.0.1/hsdb" | "hsqldb:https" | "SA" | "127.0.0.1" | 443 | "hsdb" // 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" | "derby:directory" | "APP" | null | null | "derbydb" - "jdbc:derby:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" - "jdbc:derby:memory:derbydb" | "derby:memory" | "APP" | null | null | "derbydb" - "jdbc:derby:memory:;databaseName=derbydb" | "derby:memory" | "APP" | null | null | "derbydb" - "jdbc:derby:memory:derbydb;databaseName=altdb" | "derby:memory" | "APP" | null | null | "derbydb" - "jdbc:derby:memory:derbydb;user=derbyuser;password=pw" | "derby:memory" | "derbyuser" | null | null | "derbydb" - "jdbc:derby://derbyhost:222/memory:derbydb;create=true" | "derby:network" | "APP" | "derbyhost" | 222 | "derbydb" - "jdbc:derby://derbyhost/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "derbyhost" | 1527 | "derbydb" - "jdbc:derby://127.0.0.1:1527/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "127.0.0.1" | 1527 | "derbydb" - "jdbc:derby:directory:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" - "jdbc:derby:classpath:/some/derbydb;user=derbyuser;password=pw" | "derby:classpath" | "derbyuser" | null | null | "/some/derbydb" - "jdbc:derby:jar:/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "/derbydb" - "jdbc:derby:jar:(~/path/to/db.jar)/other/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "(~/path/to/db.jar)/other/derbydb" + "jdbc:derby:derbydb" | "derby:directory" | "APP" | null | null | "derbydb" + "jdbc:derby:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" + "jdbc:derby:memory:derbydb" | "derby:memory" | "APP" | null | null | "derbydb" + "jdbc:derby:memory:;databaseName=derbydb" | "derby:memory" | "APP" | null | null | "derbydb" + "jdbc:derby:memory:derbydb;databaseName=altdb" | "derby:memory" | "APP" | null | null | "derbydb" + "jdbc:derby:memory:derbydb;user=derbyuser;password=pw" | "derby:memory" | "derbyuser" | null | null | "derbydb" + "jdbc:derby://derbyhost:222/memory:derbydb;create=true" | "derby:network" | "APP" | "derbyhost" | 222 | "derbydb" + "jdbc:derby://derbyhost/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "derbyhost" | 1527 | "derbydb" + "jdbc:derby://127.0.0.1:1527/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "127.0.0.1" | 1527 | "derbydb" + "jdbc:derby:directory:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" + "jdbc:derby:classpath:/some/derbydb;user=derbyuser;password=pw" | "derby:classpath" | "derbyuser" | null | null | "/some/derbydb" + "jdbc:derby:jar:/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "/derbydb" + "jdbc:derby:jar:(~/path/to/db.jar)/other/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "(~/path/to/db.jar)/other/derbydb" expected = new JDBCMaps.DBInfo(format, null, user, instance, null, host, port) } diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy index c903b7fc33..995d6d71f9 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy @@ -26,23 +26,23 @@ class JDBCInstrumentationTest extends AgentTestRunner { @Shared private Map jdbcUrls = [ - h2 : "jdbc:h2:mem:$dbName", - derby : "jdbc:derby:memory:$dbName", - hsqldb: "jdbc:hsqldb:mem:$dbName", + "h2:mem" : "jdbc:h2:mem:$dbName", + "derby:memory": "jdbc:derby:memory:$dbName", + "hsqldb:mem" : "jdbc:hsqldb:mem:$dbName", ] @Shared private Map jdbcDriverClassNames = [ - h2 : "org.h2.Driver", - derby : "org.apache.derby.jdbc.EmbeddedDriver", - hsqldb: "org.hsqldb.jdbc.JDBCDriver", + "h2:mem" : "org.h2.Driver", + "derby:memory": "org.apache.derby.jdbc.EmbeddedDriver", + "hsqldb:mem" : "org.hsqldb.jdbc.JDBCDriver", ] @Shared private Map jdbcUserNames = [ - h2 : null, - derby : "APP", - hsqldb: "SA", + "h2:mem" : null, + "derby:memory": "APP", + "hsqldb:mem" : "SA", ] @Shared @@ -76,7 +76,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { def createTomcatDS(String dbType, String jdbcUrl) { DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource() - def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl + def jdbcUrlToSet = dbType == "derby:memory" ? jdbcUrl + ";create=true" : jdbcUrl ds.setUrl(jdbcUrlToSet) ds.setDriverClassName(jdbcDriverClassNames.get(dbType)) String username = jdbcUserNames.get(dbType) @@ -91,7 +91,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { def createHikariDS(String dbType, String jdbcUrl) { HikariConfig config = new HikariConfig() - def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl + def jdbcUrlToSet = dbType == "derby:memory" ? jdbcUrl + ";create=true" : jdbcUrl config.setJdbcUrl(jdbcUrlToSet) String username = jdbcUserNames.get(dbType) if (username != null) { @@ -109,7 +109,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { def createC3P0DS(String dbType, String jdbcUrl) { DataSource ds = new ComboPooledDataSource() ds.setDriverClass(jdbcDriverClassNames.get(dbType)) - def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl + def jdbcUrlToSet = dbType == "derby:memory" ? jdbcUrl + ";create=true" : jdbcUrl ds.setJdbcUrl(jdbcUrlToSet) String username = jdbcUserNames.get(dbType) if (username != null) { @@ -193,22 +193,22 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "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" - "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" - "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" - "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" - "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + driver | connection | username | query + "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" + "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb:mem" | new JDBCDriver().connect(jdbcUrls.get("hsqldb:mem"), null) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), connectionProps) | null | "SELECT 3" + "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), connectionProps) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb:mem" | new JDBCDriver().connect(jdbcUrls.get("hsqldb:mem"), connectionProps) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb:mem" | cpDatasources.get("tomcat").get("hsqldb:mem").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb:mem" | cpDatasources.get("hikari").get("hsqldb:mem").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb:mem" | cpDatasources.get("c3p0").get("hsqldb:mem").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" } @Unroll @@ -256,15 +256,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "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" - "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + driver | connection | username | query + "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" + "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll @@ -311,15 +311,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "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" - "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + driver | connection | username | query + "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" + "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll @@ -366,15 +366,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "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" - "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" - "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + driver | connection | username | query + "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" + "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" + "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll @@ -421,19 +421,19 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null) | "APP" | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null) | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "CREATE TABLE S_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb" | cpDatasources.get("tomcat").get("hsqldb").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "CREATE TABLE S_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "CREATE TABLE S_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + driver | connection | username | query + "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb:mem" | new JDBCDriver().connect(jdbcUrls.get("hsqldb:mem"), null) | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "CREATE TABLE S_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "CREATE TABLE S_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb:mem" | cpDatasources.get("tomcat").get("hsqldb:mem").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "CREATE TABLE S_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "CREATE TABLE S_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb:mem" | cpDatasources.get("hikari").get("hsqldb:mem").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "CREATE TABLE S_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "CREATE TABLE S_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb:mem" | cpDatasources.get("c3p0").get("hsqldb:mem").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" } @Unroll @@ -479,15 +479,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null) | "APP" | "CREATE TABLE PS_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "CREATE TABLE PS_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "CREATE TABLE PS_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "CREATE TABLE PS_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + driver | connection | username | query + "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "CREATE TABLE PS_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "CREATE TABLE PS_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "CREATE TABLE PS_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "CREATE TABLE PS_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "CREATE TABLE PS_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "CREATE TABLE PS_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "CREATE TABLE PS_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" } @@ -558,17 +558,17 @@ class JDBCInstrumentationTest extends AgentTestRunner { } where: - prepareStatement | driver | driverClass | url | username | query - true | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" - true | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - false | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" - false | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + prepareStatement | driver | driverClass | url | username | query + true | "h2:mem" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" + true | "derby:memory" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + false | "h2:mem" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" + false | "derby:memory" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll def "#connectionPoolName connections should be cached in case of wrapped connections"() { setup: - String dbType = "hsqldb" + String dbType = "hsqldb:mem" DataSource ds = createDS(connectionPoolName, dbType, jdbcUrls.get(dbType)) String query = "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" int numQueries = 5 From 0807598d1602ddff36acf1334753d64c6a49105e Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Mon, 10 Jun 2019 12:02:14 -0700 Subject: [PATCH 03/22] Populate settings from properties and add MariaDB alt styles --- .../trace/instrumentation/jdbc/DBInfo.java | 17 + .../jdbc/DriverInstrumentation.java | 5 +- .../jdbc/JDBCConnectionUrlParser.java | 724 ++++++++++-------- .../instrumentation/jdbc/JDBCDecorator.java | 18 +- .../trace/instrumentation/jdbc/JDBCMaps.java | 15 - .../PreparedStatementInstrumentation.java | 3 +- .../jdbc/StatementInstrumentation.java | 3 +- .../groovy/JDBCConnectionUrlParserTest.groovy | 192 +++-- 8 files changed, 564 insertions(+), 413 deletions(-) create mode 100644 dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java new file mode 100644 index 0000000000..99adf4c8ff --- /dev/null +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java @@ -0,0 +1,17 @@ +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 url; + private final String user; + private final String instance; + private final String db; + private final String host; + private final Integer port; +} diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java index 7512065d5b..33c258284a 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java @@ -38,8 +38,9 @@ public final class DriverInstrumentation extends Instrumenter.Default { final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); final List parserClasses = new ArrayList<>(parsers.length + 3); + parserClasses.add(packageName + ".DBInfo"); + parserClasses.add(packageName + ".DBInfo$Builder"); parserClasses.add(packageName + ".JDBCMaps"); - parserClasses.add(packageName + ".JDBCMaps$DBInfo"); parserClasses.add(packageName + ".JDBCConnectionUrlParser"); for (final JDBCConnectionUrlParser parser : parsers) { @@ -64,7 +65,7 @@ public final class DriverInstrumentation extends Instrumenter.Default { @Advice.Argument(0) final String url, @Advice.Argument(1) final Properties props, @Advice.Return final Connection connection) { - final JDBCMaps.DBInfo dbInfo = JDBCConnectionUrlParser.parse(url, props); + final DBInfo dbInfo = JDBCConnectionUrlParser.parse(url, props); JDBCMaps.connectionInfo.put(connection, dbInfo); } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java index 8cf475eb49..7cd2453805 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java @@ -1,11 +1,12 @@ package datadog.trace.instrumentation.jdbc; -import static datadog.trace.instrumentation.jdbc.JDBCMaps.DBInfo.DEFAULT; +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; @@ -14,124 +15,125 @@ 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, plus - * the added benefit of only a single class to inject. + * 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 - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { try { // Attempt generic parsing final URI uri = new URI(jdbcUrl); - String user = uri.getUserInfo(); - String databaseName = null; - if (uri.getQuery() != null) { - final Map queryParams = splitQuery(uri.getQuery(), "&"); + populateStandardProperties(builder, splitQuery(uri.getQuery(), "&")); - if (user == null) { - user = queryParams.get("user"); - } - databaseName = queryParams.get("databasename"); + 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); + } - return new JDBCMaps.DBInfo( - uri.getScheme(), null, user, path, databaseName, uri.getHost(), uri.getPort()); + 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 DEFAULT; + return builder; } } }, MODIFIED_URL_LIKE() { - private static final String DEFAULT_HOST = "localhost"; - @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { - try { + 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 String type; - String serverName = ""; - Integer port = null; - String databaseName = null; - String instanceName = null; - String user = null; + final int hostIndex = jdbcUrl.indexOf("://"); - final int hostIndex = jdbcUrl.indexOf("://"); - - if (hostIndex <= 0) { - return DEFAULT; - } - - 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 urlProps = splitQuery(split[1], ";"); - if (urlProps.containsKey("servername")) { - serverName = urlProps.get("servername"); - } - if (urlProps.containsKey("instancename")) { - instanceName = urlProps.get("instancename"); - } - if (urlProps.containsKey("databasename")) { - databaseName = urlProps.get("databasename"); - } - if (urlProps.containsKey("user")) { - user = urlProps.get("user"); - } - } - - 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 (serverName.isEmpty()) { - serverName = DEFAULT_HOST; - } - - return new JDBCMaps.DBInfo( - type, null, user, instanceName, null /* databaseName */, serverName, port); - } catch (final UnsupportedEncodingException e) { - return DEFAULT; + 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 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); } }, @@ -140,18 +142,15 @@ public enum JDBCConnectionUrlParser { private static final int DEFAULT_PORT = 5432; @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { - final JDBCMaps.DBInfo dbInfo = GENERIC_URL_LIKE.doParse(jdbcUrl, props); - final String host = dbInfo.getHost() == null ? DEFAULT_HOST : dbInfo.getHost(); - final int port = dbInfo.getPort() <= 0 ? DEFAULT_PORT : dbInfo.getPort(); - return new JDBCMaps.DBInfo( - dbInfo.getType(), - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getInstance(), - null, - host, - port); + 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); } }, @@ -160,18 +159,123 @@ public enum JDBCConnectionUrlParser { private static final int DEFAULT_PORT = 3306; @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { - final JDBCMaps.DBInfo dbInfo = GENERIC_URL_LIKE.doParse(jdbcUrl, props); - final String host = dbInfo.getHost() == null ? DEFAULT_HOST : dbInfo.getHost(); - final int port = dbInfo.getPort() <= 0 ? DEFAULT_PORT : dbInfo.getPort(); - return new JDBCMaps.DBInfo( - dbInfo.getType(), - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getInstance(), - null, - host, - port); + 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) + .type(jdbcUrl.substring(0, 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; } }, @@ -179,43 +283,40 @@ public enum JDBCConnectionUrlParser { private static final String DEFAULT_HOST = "localhost"; @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { - final JDBCMaps.DBInfo dbInfo = GENERIC_URL_LIKE.doParse(jdbcUrl, props); - final String host = dbInfo.getHost() == null ? DEFAULT_HOST : dbInfo.getHost(); - return new JDBCMaps.DBInfo( - dbInfo.getType(), - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getDb(), - null, - host, - dbInfo.getPort()); + 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 - JDBCMaps.DBInfo doParse(String jdbcUrl, final Properties props) { + DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder) { if (jdbcUrl.startsWith("microsoft:")) { jdbcUrl = jdbcUrl.substring("microsoft:".length()); } if (!jdbcUrl.startsWith("sqlserver://")) { - return DEFAULT; + return builder; } - - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - - return new JDBCMaps.DBInfo( - "sqlserver", - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getInstance() == null ? DEFAULT_INSTANCE : dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + 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); } }, @@ -223,18 +324,12 @@ public enum JDBCConnectionUrlParser { private static final int DEFAULT_PORT = 50000; @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - return dbInfo.getPort() != null - ? dbInfo - : new JDBCMaps.DBInfo( - dbInfo.getType(), - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - DEFAULT_PORT); + 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); } }, @@ -242,31 +337,28 @@ public enum JDBCConnectionUrlParser { private static final int DEFAULT_PORT = 1521; @Override - JDBCMaps.DBInfo doParse(String jdbcUrl, final Properties props) { + DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder) { final int typeEndIndex = jdbcUrl.indexOf(":", "oracle:".length()); final String type = jdbcUrl.substring(0, typeEndIndex); jdbcUrl = jdbcUrl.substring(typeEndIndex + 1); - final JDBCMaps.DBInfo dbInfo; - if (jdbcUrl.contains("@")) { - dbInfo = ORACLE_AT.doParse(jdbcUrl, props); - } else { - dbInfo = ORACLE_CONNECT_INFO.doParse(jdbcUrl, props); + builder.type(type); + 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); } - return new JDBCMaps.DBInfo( - type, - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); } }, ORACLE_CONNECT_INFO() { @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { final String host; final Integer port; @@ -307,7 +399,7 @@ public enum JDBCConnectionUrlParser { instance = jdbcUrl.substring(instanceLoc + 1); } else { if (jdbcUrl.isEmpty()) { - return DEFAULT; + return builder; } else { host = null; port = null; @@ -315,15 +407,21 @@ public enum JDBCConnectionUrlParser { } } } - return new JDBCMaps.DBInfo(null, null, null, instance, null, host, port); + if (host != null) { + builder.host(host); + } + if (port != null) { + builder.port(port); + } + return builder.instance(instance); } }, ORACLE_AT() { @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { if (jdbcUrl.contains("@(description")) { - return ORACLE_AT_DESCRIPTION.doParse(jdbcUrl, props); + return ORACLE_AT_DESCRIPTION.doParse(jdbcUrl, builder); } final String user; @@ -345,20 +443,17 @@ public enum JDBCConnectionUrlParser { } else { hostStart = 0; } - final JDBCMaps.DBInfo dbInfo = - ORACLE_CONNECT_INFO.doParse(connectInfo.substring(hostStart), props); - - return new JDBCMaps.DBInfo( - null, - dbInfo.getUrl(), - user, - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort()); + 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*\\)"); @@ -366,41 +461,30 @@ public enum JDBCConnectionUrlParser { Pattern.compile("\\(\\s*service_name\\s*=\\s*([^ )]+)\\s*\\)"); @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { - final String user; - final String host; - final Integer port; - final String instance; - + 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) { - user = atSplit[0].substring(0, userInfoLoc); - } else { - user = null; + builder.user(atSplit[0].substring(0, userInfoLoc)); } - final String description = atSplit[1]; - final Matcher hostMatcher = HOST_REGEX.matcher(description); - final Matcher portMatcher = PORT_REGEX.matcher(description); - final Matcher instanceMatcher = INSTANCE_REGEX.matcher(description); + final Matcher hostMatcher = HOST_REGEX.matcher(atSplit[1]); if (hostMatcher.find()) { - host = hostMatcher.group(1); - } else { - host = null; + builder.host(hostMatcher.group(1)); } + + final Matcher portMatcher = PORT_REGEX.matcher(atSplit[1]); if (portMatcher.find()) { - port = Integer.parseInt(portMatcher.group(1)); - } else { - port = null; + builder.port(Integer.parseInt(portMatcher.group(1))); } + + final Matcher instanceMatcher = INSTANCE_REGEX.matcher(atSplit[1]); if (instanceMatcher.find()) { - instance = instanceMatcher.group(1); - } else { - instance = null; + builder.instance(instanceMatcher.group(1)); } - return new JDBCMaps.DBInfo(null, null, user, instance, null, host, port); + + return builder; } }, @@ -408,9 +492,9 @@ public enum JDBCConnectionUrlParser { private static final int DEFAULT_PORT = 8082; @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { String type = "h2"; - String instance = null; + final String instance; final String h2Url = jdbcUrl.substring("h2:".length()); if (h2Url.startsWith("mem:")) { @@ -438,27 +522,17 @@ public enum JDBCConnectionUrlParser { instance = h2Url.substring("zip:".length()); } } else if (h2Url.startsWith("tcp:")) { - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - - return new JDBCMaps.DBInfo( - "h2:tcp", - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + final DBInfo dbInfo = builder.build(); + if (dbInfo.getPort() == null) { + builder.port(DEFAULT_PORT); + } + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("h2:tcp"); } else if (h2Url.startsWith("ssl:")) { - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - - return new JDBCMaps.DBInfo( - "h2:ssl", - dbInfo.getUrl(), - dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); + final DBInfo dbInfo = builder.build(); + if (dbInfo.getPort() == null) { + builder.port(DEFAULT_PORT); + } + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("h2:ssl"); } else { type = "h2:file"; final int propLoc = h2Url.indexOf(";"); @@ -468,7 +542,10 @@ public enum JDBCConnectionUrlParser { instance = h2Url; } } - return new JDBCMaps.DBInfo(type, null, null, instance, null, null, null); + if (!instance.isEmpty()) { + builder.instance(instance); + } + return builder.type(type); } }, @@ -477,9 +554,13 @@ public enum JDBCConnectionUrlParser { private static final int DEFAULT_PORT = 9001; @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { + DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { String type = "hsqldb"; 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:")) { type = "hsqldb:mem"; @@ -491,58 +572,30 @@ public enum JDBCConnectionUrlParser { type = "hsqldb:res"; instance = hsqlUrl.substring("res:".length()); } else if (hsqlUrl.startsWith("hsql:")) { - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - - return new JDBCMaps.DBInfo( - "hsqldb:hsql", - dbInfo.getUrl(), - dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); - + if (dbInfo.getPort() == null) { + builder.port(DEFAULT_PORT); + } + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb:hsql"); } else if (hsqlUrl.startsWith("hsqls:")) { - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - - return new JDBCMaps.DBInfo( - "hsqldb:hsqls", - dbInfo.getUrl(), - dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? DEFAULT_PORT : dbInfo.getPort()); - + if (dbInfo.getPort() == null) { + builder.port(DEFAULT_PORT); + } + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb:hsqls"); } else if (hsqlUrl.startsWith("http:")) { - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - - return new JDBCMaps.DBInfo( - "hsqldb:http", - dbInfo.getUrl(), - dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? 80 : dbInfo.getPort()); - + if (dbInfo.getPort() == null) { + builder.port(80); + } + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb:http"); } else if (hsqlUrl.startsWith("https:")) { - final JDBCMaps.DBInfo dbInfo = MODIFIED_URL_LIKE.doParse(jdbcUrl, props); - - return new JDBCMaps.DBInfo( - "hsqldb:https", - dbInfo.getUrl(), - dbInfo.getUser() == null ? DEFAULT_USER : dbInfo.getUser(), - dbInfo.getInstance(), - dbInfo.getDb(), - dbInfo.getHost(), - dbInfo.getPort() == null ? 443 : dbInfo.getPort()); - + if (dbInfo.getPort() == null) { + builder.port(443); + } + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb:https"); } else { type = "hsqldb:mem"; instance = hsqlUrl; } - return new JDBCMaps.DBInfo(type, null, DEFAULT_USER, instance, null, null, null); + return builder.type(type).instance(instance); } }, @@ -551,37 +604,21 @@ public enum JDBCConnectionUrlParser { private static final int DEFAULT_PORT = 1527; @Override - JDBCMaps.DBInfo doParse(final String jdbcUrl, final Properties props) { - String type = "derby"; + DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { + final String type; String instance = null; String host = null; - Integer port = null; - String user = DEFAULT_USER; - if (props != null) { - instance = props.getProperty("databasename", instance); - user = props.getProperty("user", user); + 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) { - try { - final Map urlProps = splitQuery(split[1], ";"); - if (urlProps.containsKey("databasename")) { - instance = urlProps.get("databasename"); - } - if (urlProps.containsKey("user")) { - user = urlProps.get("user"); - } - if (urlProps.containsKey("servername")) { - host = urlProps.get("servername"); - } - if (urlProps.containsKey("portnumber")) { - port = Integer.parseInt(urlProps.get("portnumber")); - } - } catch (final Exception e) { - } + populateStandardProperties(builder, splitQuery(split[1], ";")); } if (split[0].startsWith("memory:")) { @@ -610,6 +647,9 @@ public enum JDBCConnectionUrlParser { } } else if (split[0].startsWith("//")) { type = "derby:network"; + if (dbInfo.getPort() == null) { + builder.port(DEFAULT_PORT); + } String url = split[0].substring("//".length()); final int instanceLoc = url.indexOf("/"); if (instanceLoc >= 0) { @@ -623,10 +663,9 @@ public enum JDBCConnectionUrlParser { final int portLoc = url.indexOf(":"); if (portLoc > 0) { host = url.substring(0, portLoc); - port = Integer.parseInt(url.substring(portLoc + 1)); + builder.port(Integer.parseInt(url.substring(portLoc + 1))); } else { host = url; - port = DEFAULT_PORT; } } else { type = "derby:directory"; @@ -636,7 +675,10 @@ public enum JDBCConnectionUrlParser { } } - return new JDBCMaps.DBInfo(type, null, user, instance, null, host, port); + if (host != null) { + builder.host(host); + } + return builder.type(type).instance(instance); } }; @@ -656,9 +698,9 @@ public enum JDBCConnectionUrlParser { this.typeKeys = typeKeys; } - abstract JDBCMaps.DBInfo doParse(String jdbcUrl, final Properties props); + abstract DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder); - public static JDBCMaps.DBInfo parse(String connectionUrl, final Properties props) { + public static DBInfo parse(String connectionUrl, final Properties props) { if (connectionUrl == null) { return DEFAULT; } @@ -679,34 +721,84 @@ public enum JDBCConnectionUrlParser { 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, props); + return typeParsers.get(baseType).doParse(jdbcUrl, parsedProps).build(); } - return GENERIC_URL_LIKE.doParse(connectionUrl, props); + return GENERIC_URL_LIKE.doParse(connectionUrl, parsedProps).build(); } catch (final Exception e) { ExceptionLogger.LOGGER.debug("Error parsing URL", e); - return DEFAULT; + return parsedProps.build(); } } // Source: https://stackoverflow.com/a/13592567 - private static Map splitQuery(final String query, final String separator) - throws UnsupportedEncodingException { + private static Map splitQuery(final String query, final String separator) { + if (query == null || query.isEmpty()) { + return Collections.emptyMap(); + } final Map query_pairs = new LinkedHashMap<>(); final String[] pairs = query.split(separator); for (final String pair : pairs) { - 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); + 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 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); + } + } + } + } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java index 213e18398c..8540f2ab67 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java @@ -10,9 +10,11 @@ import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.SQLException; -public class JDBCDecorator extends DatabaseClientDecorator { +public class JDBCDecorator extends DatabaseClientDecorator { public static final JDBCDecorator DECORATE = new JDBCDecorator(); + private static final String DB_QUERY = "DB Query"; + @Override protected String[] instrumentationNames() { return new String[] {"jdbc"}; @@ -39,17 +41,17 @@ public class JDBCDecorator extends DatabaseClientDecorator { } @Override - protected String dbUser(final JDBCMaps.DBInfo info) { + protected String dbUser(final DBInfo info) { return info.getUser(); } @Override - protected String dbInstance(final JDBCMaps.DBInfo info) { + protected String dbInstance(final DBInfo info) { return info.getInstance(); } 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 not created via * Driver.connect, or it has never seen before, the connectionInfo map will return null and will @@ -65,10 +67,10 @@ public class JDBCDecorator extends DatabaseClientDecorator { if (url != null) { dbInfo = JDBCConnectionUrlParser.parse(url, connection.getClientInfo()); } else { - dbInfo = JDBCMaps.DBInfo.DEFAULT; + dbInfo = DBInfo.DEFAULT; } } catch (final SQLException se) { - dbInfo = JDBCMaps.DBInfo.DEFAULT; + dbInfo = DBInfo.DEFAULT; } JDBCMaps.connectionInfo.put(connection, dbInfo); } @@ -83,7 +85,7 @@ public class JDBCDecorator extends DatabaseClientDecorator { @Override 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); Tags.COMPONENT.set(span, "java-jdbc-statement"); return super.onStatement(span, statement); @@ -91,7 +93,7 @@ public class JDBCDecorator extends DatabaseClientDecorator { public Span onPreparedStatement(final Span span, final PreparedStatement 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); Tags.COMPONENT.set(span, "java-jdbc-prepared_statement"); return super.onStatement(span, sql); diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java index d4b4135125..1d91031775 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCMaps.java @@ -5,7 +5,6 @@ import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap; import datadog.trace.bootstrap.WeakMap; import java.sql.Connection; import java.sql.PreparedStatement; -import lombok.Data; /** * JDBC instrumentation shares a global map of connection info. @@ -15,18 +14,4 @@ import lombok.Data; public class JDBCMaps { public static final WeakMap connectionInfo = newWeakMap(); public static final WeakMap preparedStatements = newWeakMap(); - - public static final String DB_QUERY = "DB Query"; - - @Data - public static class DBInfo { - public static DBInfo DEFAULT = new DBInfo("database", null, null, null, null, null, null); - private final String type; - private final String url; - private final String user; - private final String instance; - private final String db; - private final String host; - private final Integer port; - } } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java index e457cb2000..1b4b0d7da6 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java @@ -45,9 +45,10 @@ public final class PreparedStatementInstrumentation extends Instrumenter.Default final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); final List parserClasses = new ArrayList<>(parsers.length + 8); + parserClasses.add(packageName + ".DBInfo"); + parserClasses.add(packageName + ".DBInfo$Builder"); parserClasses.add(packageName + ".JDBCUtils"); parserClasses.add(packageName + ".JDBCMaps"); - parserClasses.add(packageName + ".JDBCMaps$DBInfo"); parserClasses.add(packageName + ".JDBCConnectionUrlParser"); parserClasses.add("datadog.trace.agent.decorator.BaseDecorator"); diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java index a890fddece..9d513319e9 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java @@ -45,9 +45,10 @@ public final class StatementInstrumentation extends Instrumenter.Default { final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); final List parserClasses = new ArrayList<>(parsers.length + 8); + parserClasses.add(packageName + ".DBInfo"); + parserClasses.add(packageName + ".DBInfo$Builder"); parserClasses.add(packageName + ".JDBCUtils"); parserClasses.add(packageName + ".JDBCMaps"); - parserClasses.add(packageName + ".JDBCMaps$DBInfo"); parserClasses.add(packageName + ".JDBCConnectionUrlParser"); parserClasses.add("datadog.trace.agent.decorator.BaseDecorator"); diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy index 8707645ebe..b6efb455b0 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy @@ -1,13 +1,30 @@ -import datadog.trace.instrumentation.jdbc.JDBCMaps +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) == JDBCMaps.DBInfo.DEFAULT + parse(url, null) == DBInfo.DEFAULT where: url | _ @@ -18,7 +35,7 @@ class JDBCConnectionUrlParserTest extends Specification { def "verify #format parsing of #url"() { setup: - def info = parse(url, null) + def info = parse(url, props) expect: info.url == expected.url @@ -31,97 +48,132 @@ class JDBCConnectionUrlParserTest extends Specification { info == expected where: - url | format | user | host | port | instance + url | props | format | user | host | port | instance | db // https://jdbc.postgresql.org/documentation/94/connect.html - "jdbc:postgresql:///" | "postgresql" | null | "localhost" | 5432 | "" - "jdbc:postgresql://pghost" | "postgresql" | null | "pghost" | 5432 | "" - "jdbc:postgresql://pghost:11/pgdb?user=pguser&password=PW" | "postgresql" | "pguser" | "pghost" | 11 | "pgdb" + "jdbc:postgresql:///" | null | "postgresql" | null | "localhost" | 5432 | null | null + "jdbc:postgresql:///" | stdProps | "postgresql" | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName" + "jdbc:postgresql://pg.host" | null | "postgresql" | null | "pg.host" | 5432 | null | null + "jdbc:postgresql://pg.host:11/pgdb?user=pguser&password=PW" | null | "postgresql" | "pguser" | "pg.host" | 11 | null | "pgdb" + "jdbc:postgresql://pg.host:11/pgdb?user=pguser&password=PW" | stdProps | "postgresql" | "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:///" | "mysql" | null | "localhost" | 3306 | "" - "jdbc:mysql://myhost" | "mysql" | null | "myhost" | 3306 | "" - "jdbc:mysql://myhost?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 3306 | "" - "jdbc:mysql://myhost:22/mydb?user=myuser&password=PW" | "mysql" | "myuser" | "myhost" | 22 | "mydb" - "jdbc:mariadb://mdbhost:33/mdbdb?user=mdbuser&password=PW" | "mariadb" | "mdbuser" | "mdbhost" | 33 | "mdbdb" + "jdbc:mysql:///" | null | "mysql" | null | "localhost" | 3306 | null | null + "jdbc:mysql:///" | stdProps | "mysql" | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName" + "jdbc:mysql://my.host" | null | "mysql" | null | "my.host" | 3306 | null | null + "jdbc:mysql://my.host?user=myuser&password=PW" | null | "mysql" | "myuser" | "my.host" | 3306 | null | null + "jdbc:mysql://my.host:22/mydb?user=myuser&password=PW" | null | "mysql" | "myuser" | "my.host" | 22 | null | "mydb" + "jdbc:mysql://127.0.0.1:22/mydb?user=myuser&password=PW" | stdProps | "mysql" | "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 | "127.0.0.1" | 33 | null | "mdbdb" + "jdbc:mariadb:localhost/mdbdb" | null | "mariadb" | null | "localhost" | 3306 | null | "mdbdb" + "jdbc:mariadb:localhost/mdbdb?user=mdbuser&password=PW" | stdProps | "mariadb" | "mdbuser" | "localhost" | 9999 | null | "mdbdb" + "jdbc:mariadb:localhost:33/mdbdb" | stdProps | "mariadb" | "stdUserName" | "localhost" | 33 | null | "mdbdb" + "jdbc:mariadb://mdb.host:33/mdbdb?user=mdbuser&password=PW" | null | "mariadb" | "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://;" | "sqlserver" | null | "localhost" | 1433 | "MSSQLSERVER" - "jdbc:sqlserver://sshost\\ssinstance:44;databaseName=ssdb;user=ssuser;password=pw" | "sqlserver" | "ssuser" | "sshost" | 44 | "ssinstance" - "jdbc:sqlserver://;serverName=sshost\\ssinstance:44;DatabaseName=;" | "sqlserver" | null | "sshost" | 44 | "ssinstance" - "jdbc:sqlserver://sshost;serverName=althost;DatabaseName=ssdb;" | "sqlserver" | null | "sshost" | 1433 | "MSSQLSERVER" - "jdbc:microsoft:sqlserver://sshost:44;DatabaseName=ssdb;user=ssuser;password=pw;user=ssuser2;" | "sqlserver" | "ssuser" | "sshost" | 44 | "MSSQLSERVER" + "jdbc:microsoft:sqlserver://;" | null | "sqlserver" | null | "localhost" | 1433 | "MSSQLSERVER" | null + "jdbc:microsoft:sqlserver://;" | stdProps | "sqlserver" | "stdUserName" | "stdServerName" | 9999 | "MSSQLSERVER" | "stdDatabaseName" + "jdbc:sqlserver://ss.host\\ssinstance:44;databaseName=ssdb;user=ssuser;password=pw" | null | "sqlserver" | "ssuser" | "ss.host" | 44 | "ssinstance" | "ssdb" + "jdbc:sqlserver://;serverName=ss.host\\ssinstance:44;DatabaseName=;" | null | "sqlserver" | null | "ss.host" | 44 | "ssinstance" | null + "jdbc:sqlserver://ss.host;serverName=althost;DatabaseName=ssdb;" | null | "sqlserver" | null | "ss.host" | 1433 | "MSSQLSERVER" | "ssdb" + "jdbc:microsoft:sqlserver://ss.host:44;DatabaseName=ssdb;user=ssuser;password=pw;user=ssuser2;" | null | "sqlserver" | "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" | "oracle:thin" | "orcluser" | "localhost" | 55 | "orclsn" - "jdbc:oracle:thin:orcluser/PW@//orclhost:55/orclsn" | "oracle:thin" | "orcluser" | "orclhost" | 55 | "orclsn" - "jdbc:oracle:thin:orcluser/PW@127.0.0.1:orclsn" | "oracle:thin" | "orcluser" | "127.0.0.1" | 1521 | "orclsn" - "jdbc:oracle:thin:orcluser/PW@//orclhost/orclsn" | "oracle:thin" | "orcluser" | "orclhost" | 1521 | "orclsn" - "jdbc:oracle:thin:@//orclhost:55/orclsn" | "oracle:thin" | null | "orclhost" | 55 | "orclsn" - "jdbc:oracle:thin:@ldap://orclhost:55/some,cn=OracleContext,dc=com" | "oracle:thin" | null | "orclhost" | 55 | "some,cn=oraclecontext,dc=com" - "jdbc:oracle:thin:127.0.0.1:orclsn" | "oracle:thin" | null | "127.0.0.1" | 1521 | "orclsn" + "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)))" | "oracle:thin" | null | "127.0.0.1" | 1521 | "orclsn" + "(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@orclhost:55/orclsn" | "oracle:drivertype" | "orcluser" | "orclhost" | 55 | "orclsn" - "jdbc:oracle:oci8:@" | "oracle:oci8" | null | null | 1521 | null - "jdbc:oracle:oci8:@orclsn" | "oracle:oci8" | null | null | 1521 | "orclsn" - "jdbc:oracle:oci:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)( HOST = orclhost )" + - "( PORT = 55 ))(CONNECT_DATA=(SERVICE_NAME =orclsn )))" | "oracle:oci" | null | "orclhost" | 55 | "orclsn" + "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:as400://ashost:66/asdb:user=asuser;password=PW;" | "as400" | "asuser" | "ashost" | 66 | "asdb" - "jdbc:db2://db2host:77/db2db:user=db2user;password=PW;" | "db2" | "db2user" | "db2host" | 77 | "db2db" + "jdbc:db2://db2.host" | null | "db2" | null | "db2.host" | 50000 | null | null + "jdbc:db2://db2.host" | stdProps | "db2" | "stdUserName" | "db2.host" | 9999 | null | "stdDatabaseName" + "jdbc:db2://db2.host:77/db2db:user=db2user;password=PW;" | null | "db2" | "db2user" | "db2.host" | 77 | "db2db" | null + "jdbc:db2://db2.host:77/db2db:user=db2user;password=PW;" | stdProps | "db2" | "db2user" | "db2.host" | 77 | "db2db" | "stdDatabaseName" + "jdbc:as400://ashost:66/asdb:user=asuser;password=PW;" | null | "as400" | "asuser" | "ashost" | 66 | "asdb" | null // https://help.sap.com/viewer/0eec0d68141541d1b07893a39944924e/2.0.03/en-US/ff15928cf5594d78b841fbbe649f04b4.html - "jdbc:sap://saphost:88/?databaseName=sapdb&user=sapuser&password=PW" | "sap" | "sapuser" | "saphost" | 88 | "sapdb" + "jdbc:sap://sap.host" | null | "sap" | null | "sap.host" | null | null | null + "jdbc:sap://sap.host" | stdProps | "sap" | "stdUserName" | "sap.host" | 9999 | null | "stdDatabaseName" + "jdbc:sap://sap.host:88/?databaseName=sapdb&user=sapuser&password=PW" | null | "sap" | "sapuser" | "sap.host" | 88 | null | "sapdb" // TODO: -// "jdbc:informix-sqli://infxhost:99/infxdb:INFORMIXSERVER=infxsn;user=infxuser;password=PW" | "informix-sqli" | "infxuser" | "infxhost" | 99 | "infxdb" -// "jdbc:informix-direct://infxdb:999;user=infxuser;password=PW" | "informix-direct" | "infxuser" | "infxhost" | 999 | "infxdb" +// "jdbc:informix-sqli://infxhost:99/infxdb:INFORMIXSERVER=infxsn;user=infxuser;password=PW" | null | "informix-sqli" | "infxuser" | "infxhost" | 99 | "infxdb"| null +// "jdbc:informix-direct://infxdb:999;user=infxuser;password=PW" | null | "informix-direct" | "infxuser" | "infxhost" | 999 | "infxdb"| null // http://www.h2database.com/html/features.html#database_url - "jdbc:h2:mem:" | "h2:mem" | null | null | null | "" - "jdbc:h2:mem:h2db" | "h2:mem" | null | null | null | "h2db" - "jdbc:h2:tcp://h2host:111/path/h2db;user=h2user;password=PW" | "h2:tcp" | "h2user" | "h2host" | 111 | "path/h2db" - "jdbc:h2:ssl://h2host:111/path/h2db;user=h2user;password=PW" | "h2:ssl" | "h2user" | "h2host" | 111 | "path/h2db" - "jdbc:h2:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" - "jdbc:h2:file:~/h2file;USER=h2user;PASSWORD=PW" | "h2:file" | null | null | null | "~/h2file" - "jdbc:h2:file:/data/h2file" | "h2:file" | null | null | null | "/data/h2file" - "jdbc:h2:file:C:/data/h2file" | "h2:file" | null | null | null | "c:/data/h2file" - "jdbc:h2:zip:~/db.zip!/h2zip" | "h2:zip" | null | null | null | "~/db.zip!/h2zip" + "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" | "hsqldb:mem" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:mem:hsdb" | "hsqldb:mem" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:file:hsdb" | "hsqldb:file" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:file:/loc/hsdb" | "hsqldb:file" | "SA" | null | null | "/loc/hsdb" - "jdbc:hsqldb:file:C:/hsdb" | "hsqldb:file" | "SA" | null | null | "c:/hsdb" - "jdbc:hsqldb:res:hsdb" | "hsqldb:res" | "SA" | null | null | "hsdb" - "jdbc:hsqldb:res:/cp/hsdb" | "hsqldb:res" | "SA" | null | null | "/cp/hsdb" - "jdbc:hsqldb:hsql://hshost:333/hsdb" | "hsqldb:hsql" | "SA" | "hshost" | 333 | "hsdb" - "jdbc:hsqldb:hsqls://hshost/hsdb" | "hsqldb:hsqls" | "SA" | "hshost" | 9001 | "hsdb" - "jdbc:hsqldb:http://hshost" | "hsqldb:http" | "SA" | "hshost" | 80 | null - "jdbc:hsqldb:http://hshost:333/hsdb" | "hsqldb:http" | "SA" | "hshost" | 333 | "hsdb" - "jdbc:hsqldb:https://127.0.0.1/hsdb" | "hsqldb:https" | "SA" | "127.0.0.1" | 443 | "hsdb" + "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" | "derby:directory" | "APP" | null | null | "derbydb" - "jdbc:derby:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" - "jdbc:derby:memory:derbydb" | "derby:memory" | "APP" | null | null | "derbydb" - "jdbc:derby:memory:;databaseName=derbydb" | "derby:memory" | "APP" | null | null | "derbydb" - "jdbc:derby:memory:derbydb;databaseName=altdb" | "derby:memory" | "APP" | null | null | "derbydb" - "jdbc:derby:memory:derbydb;user=derbyuser;password=pw" | "derby:memory" | "derbyuser" | null | null | "derbydb" - "jdbc:derby://derbyhost:222/memory:derbydb;create=true" | "derby:network" | "APP" | "derbyhost" | 222 | "derbydb" - "jdbc:derby://derbyhost/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "derbyhost" | 1527 | "derbydb" - "jdbc:derby://127.0.0.1:1527/memory:derbydb;create=true;user=derbyuser;password=pw" | "derby:network" | "derbyuser" | "127.0.0.1" | 1527 | "derbydb" - "jdbc:derby:directory:derbydb;user=derbyuser;password=pw" | "derby:directory" | "derbyuser" | null | null | "derbydb" - "jdbc:derby:classpath:/some/derbydb;user=derbyuser;password=pw" | "derby:classpath" | "derbyuser" | null | null | "/some/derbydb" - "jdbc:derby:jar:/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "/derbydb" - "jdbc:derby:jar:(~/path/to/db.jar)/other/derbydb;user=derbyuser;password=pw" | "derby:jar" | "derbyuser" | null | null | "(~/path/to/db.jar)/other/derbydb" + "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 JDBCMaps.DBInfo(format, null, user, instance, null, host, port) + expected = new DBInfo.Builder().type(format).user(user).instance(instance).db(db).host(host).port(port).build() } } From 288add2a60ae98a65063be7f0b7f73b2abe69a32 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Mon, 10 Jun 2019 15:20:56 -0700 Subject: [PATCH 04/22] Fix slickdb test --- .../java-concurrent/src/slickTest/groovy/SlickTest.groovy | 2 +- .../java-concurrent/src/slickTest/scala/SlickUtils.scala | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/dd-java-agent/instrumentation/java-concurrent/src/slickTest/groovy/SlickTest.groovy b/dd-java-agent/instrumentation/java-concurrent/src/slickTest/groovy/SlickTest.groovy index 99f16f99ae..6bd75f2787 100644 --- a/dd-java-agent/instrumentation/java-concurrent/src/slickTest/groovy/SlickTest.groovy +++ b/dd-java-agent/instrumentation/java-concurrent/src/slickTest/groovy/SlickTest.groovy @@ -42,7 +42,7 @@ class SlickTest extends AgentTestRunner { "$Tags.DB_TYPE.key" SlickUtils.Driver() "$Tags.DB_USER.key" SlickUtils.Username() - "db.instance" SlickUtils.Url() + "db.instance" SlickUtils.Db() "span.origin.type" "org.h2.jdbc.JdbcPreparedStatement" defaultTags() diff --git a/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala b/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala index c1cb3514f0..85a742bbb4 100644 --- a/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala +++ b/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala @@ -34,9 +34,10 @@ class SlickUtils { object SlickUtils { - val Driver = "h2" + val Driver = "h2:mem" + val Db = "test" val Username = "TESTUSER" - val Url = s"jdbc:${Driver}:mem:test" + val Url = s"jdbc:${Driver}:${Db}" val TestValue = 3 val TestQuery = "SELECT 3" From 4469c7fb119df0f4c1007ab4242a4fa03432a065 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Mon, 10 Jun 2019 16:17:36 -0700 Subject: [PATCH 05/22] separate out driver subtype to avoid changing existing service names --- .../src/slickTest/scala/SlickUtils.scala | 4 +- .../trace/instrumentation/jdbc/DBInfo.java | 1 + .../jdbc/JDBCConnectionUrlParser.java | 55 +++-- .../groovy/JDBCConnectionUrlParserTest.groovy | 194 +++++++++--------- .../groovy/JDBCInstrumentationTest.groovy | 166 +++++++-------- 5 files changed, 209 insertions(+), 211 deletions(-) diff --git a/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala b/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala index 85a742bbb4..1bc64c19e9 100644 --- a/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala +++ b/dd-java-agent/instrumentation/java-concurrent/src/slickTest/scala/SlickUtils.scala @@ -34,10 +34,10 @@ class SlickUtils { object SlickUtils { - val Driver = "h2:mem" + val Driver = "h2" val Db = "test" val Username = "TESTUSER" - val Url = s"jdbc:${Driver}:${Db}" + val Url = s"jdbc:${Driver}:mem:${Db}" val TestValue = 3 val TestQuery = "SELECT 3" diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java index 99adf4c8ff..d59e343c72 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DBInfo.java @@ -8,6 +8,7 @@ import lombok.Data; 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; diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java index 7cd2453805..ba5cdf4a30 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java @@ -172,7 +172,7 @@ public enum JDBCConnectionUrlParser { if (protoLoc > typeEndLoc) { return MARIA_SUBPROTO .doParse(jdbcUrl.substring(protoLoc + 3), builder) - .type(jdbcUrl.substring(0, protoLoc)); + .subtype(jdbcUrl.substring(typeEndLoc + 1, protoLoc)); } if (protoLoc > 0) { return GENERIC_URL_LIKE.doParse(jdbcUrl, builder); @@ -339,10 +339,10 @@ public enum JDBCConnectionUrlParser { @Override DBInfo.Builder doParse(String jdbcUrl, final DBInfo.Builder builder) { final int typeEndIndex = jdbcUrl.indexOf(":", "oracle:".length()); - final String type = jdbcUrl.substring(0, typeEndIndex); + final String subtype = jdbcUrl.substring("oracle:".length(), typeEndIndex); jdbcUrl = jdbcUrl.substring(typeEndIndex + 1); - builder.type(type); + builder.subtype(subtype); final DBInfo dbInfo = builder.build(); if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); @@ -493,12 +493,11 @@ public enum JDBCConnectionUrlParser { @Override DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { - String type = "h2"; final String instance; final String h2Url = jdbcUrl.substring("h2:".length()); if (h2Url.startsWith("mem:")) { - type = "h2:mem"; + builder.subtype("mem"); final int propLoc = h2Url.indexOf(";"); if (propLoc >= 0) { instance = h2Url.substring("mem:".length(), propLoc); @@ -506,7 +505,7 @@ public enum JDBCConnectionUrlParser { instance = h2Url.substring("mem:".length()); } } else if (h2Url.startsWith("file:")) { - type = "h2:file"; + builder.subtype("file"); final int propLoc = h2Url.indexOf(";"); if (propLoc >= 0) { instance = h2Url.substring("file:".length(), propLoc); @@ -514,7 +513,7 @@ public enum JDBCConnectionUrlParser { instance = h2Url.substring("file:".length()); } } else if (h2Url.startsWith("zip:")) { - type = "h2:zip"; + builder.subtype("zip"); final int propLoc = h2Url.indexOf(";"); if (propLoc >= 0) { instance = h2Url.substring("zip:".length(), propLoc); @@ -526,15 +525,15 @@ public enum JDBCConnectionUrlParser { if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); } - return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("h2:tcp"); + 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:ssl"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("h2").subtype("ssl"); } else { - type = "h2:file"; + builder.subtype("file"); final int propLoc = h2Url.indexOf(";"); if (propLoc >= 0) { instance = h2Url.substring(0, propLoc); @@ -545,7 +544,7 @@ public enum JDBCConnectionUrlParser { if (!instance.isEmpty()) { builder.instance(instance); } - return builder.type(type); + return builder; } }, @@ -555,7 +554,6 @@ public enum JDBCConnectionUrlParser { @Override DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { - String type = "hsqldb"; String instance = null; final DBInfo dbInfo = builder.build(); if (dbInfo.getUser() == null) { @@ -563,39 +561,39 @@ public enum JDBCConnectionUrlParser { } final String hsqlUrl = jdbcUrl.substring("hsqldb:".length()); if (hsqlUrl.startsWith("mem:")) { - type = "hsqldb:mem"; + builder.subtype("mem"); instance = hsqlUrl.substring("mem:".length()); } else if (hsqlUrl.startsWith("file:")) { - type = "hsqldb:file"; + builder.subtype("file"); instance = hsqlUrl.substring("file:".length()); } else if (hsqlUrl.startsWith("res:")) { - type = "hsqldb: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:hsql"); + 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:hsqls"); + 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:http"); + 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:https"); + return MODIFIED_URL_LIKE.doParse(jdbcUrl, builder).type("hsqldb").subtype("https"); } else { - type = "hsqldb:mem"; + builder.subtype("mem"); instance = hsqlUrl; } - return builder.type(type).instance(instance); + return builder.instance(instance); } }, @@ -605,7 +603,6 @@ public enum JDBCConnectionUrlParser { @Override DBInfo.Builder doParse(final String jdbcUrl, final DBInfo.Builder builder) { - final String type; String instance = null; String host = null; @@ -622,31 +619,31 @@ public enum JDBCConnectionUrlParser { } if (split[0].startsWith("memory:")) { - type = "derby:memory"; + builder.subtype("memory"); final String urlInstance = split[0].substring("memory:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } } else if (split[0].startsWith("directory:")) { - type = "derby:directory"; + builder.subtype("directory"); final String urlInstance = split[0].substring("directory:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } } else if (split[0].startsWith("classpath:")) { - type = "derby:classpath"; + builder.subtype("classpath"); final String urlInstance = split[0].substring("classpath:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } } else if (split[0].startsWith("jar:")) { - type = "derby:jar"; + builder.subtype("jar"); final String urlInstance = split[0].substring("jar:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } } else if (split[0].startsWith("//")) { - type = "derby:network"; + builder.subtype("network"); if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); } @@ -668,7 +665,7 @@ public enum JDBCConnectionUrlParser { host = url; } } else { - type = "derby:directory"; + builder.subtype("directory"); final String urlInstance = split[0]; if (!urlInstance.isEmpty()) { instance = urlInstance; @@ -678,7 +675,7 @@ public enum JDBCConnectionUrlParser { if (host != null) { builder.host(host); } - return builder.type(type).instance(instance); + return builder.instance(instance); } }; diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy index b6efb455b0..3e70935b54 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy @@ -33,7 +33,7 @@ class JDBCConnectionUrlParserTest extends Specification { "bogus:string" | _ } - def "verify #format parsing of #url"() { + def "verify #type:#subtype parsing of #url"() { setup: def info = parse(url, props) @@ -48,132 +48,132 @@ class JDBCConnectionUrlParserTest extends Specification { info == expected where: - url | props | format | user | host | port | instance | db + url | props | type | subtype | user | host | port | instance | db // https://jdbc.postgresql.org/documentation/94/connect.html - "jdbc:postgresql:///" | null | "postgresql" | null | "localhost" | 5432 | null | null - "jdbc:postgresql:///" | stdProps | "postgresql" | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName" - "jdbc:postgresql://pg.host" | null | "postgresql" | null | "pg.host" | 5432 | null | null - "jdbc:postgresql://pg.host:11/pgdb?user=pguser&password=PW" | null | "postgresql" | "pguser" | "pg.host" | 11 | null | "pgdb" - "jdbc:postgresql://pg.host:11/pgdb?user=pguser&password=PW" | stdProps | "postgresql" | "pguser" | "pg.host" | 11 | null | "pgdb" + "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 | "localhost" | 3306 | null | null - "jdbc:mysql:///" | stdProps | "mysql" | "stdUserName" | "stdServerName" | 9999 | null | "stdDatabaseName" - "jdbc:mysql://my.host" | null | "mysql" | null | "my.host" | 3306 | null | null - "jdbc:mysql://my.host?user=myuser&password=PW" | null | "mysql" | "myuser" | "my.host" | 3306 | null | null - "jdbc:mysql://my.host:22/mydb?user=myuser&password=PW" | null | "mysql" | "myuser" | "my.host" | 22 | null | "mydb" - "jdbc:mysql://127.0.0.1:22/mydb?user=myuser&password=PW" | stdProps | "mysql" | "myuser" | "127.0.0.1" | 22 | null | "mydb" + "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 | "127.0.0.1" | 33 | null | "mdbdb" - "jdbc:mariadb:localhost/mdbdb" | null | "mariadb" | null | "localhost" | 3306 | null | "mdbdb" - "jdbc:mariadb:localhost/mdbdb?user=mdbuser&password=PW" | stdProps | "mariadb" | "mdbuser" | "localhost" | 9999 | null | "mdbdb" - "jdbc:mariadb:localhost:33/mdbdb" | stdProps | "mariadb" | "stdUserName" | "localhost" | 33 | null | "mdbdb" - "jdbc:mariadb://mdb.host:33/mdbdb?user=mdbuser&password=PW" | null | "mariadb" | "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: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" + "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" + "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 | "localhost" | 1433 | "MSSQLSERVER" | null - "jdbc:microsoft:sqlserver://;" | stdProps | "sqlserver" | "stdUserName" | "stdServerName" | 9999 | "MSSQLSERVER" | "stdDatabaseName" - "jdbc:sqlserver://ss.host\\ssinstance:44;databaseName=ssdb;user=ssuser;password=pw" | null | "sqlserver" | "ssuser" | "ss.host" | 44 | "ssinstance" | "ssdb" - "jdbc:sqlserver://;serverName=ss.host\\ssinstance:44;DatabaseName=;" | null | "sqlserver" | null | "ss.host" | 44 | "ssinstance" | null - "jdbc:sqlserver://ss.host;serverName=althost;DatabaseName=ssdb;" | null | "sqlserver" | null | "ss.host" | 1433 | "MSSQLSERVER" | "ssdb" - "jdbc:microsoft:sqlserver://ss.host:44;DatabaseName=ssdb;user=ssuser;password=pw;user=ssuser2;" | null | "sqlserver" | "ssuser" | "ss.host" | 44 | "MSSQLSERVER" | "ssdb" + "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: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 + "(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: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 + "( 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 | "db2.host" | 50000 | null | null - "jdbc:db2://db2.host" | stdProps | "db2" | "stdUserName" | "db2.host" | 9999 | null | "stdDatabaseName" - "jdbc:db2://db2.host:77/db2db:user=db2user;password=PW;" | null | "db2" | "db2user" | "db2.host" | 77 | "db2db" | null - "jdbc:db2://db2.host:77/db2db:user=db2user;password=PW;" | stdProps | "db2" | "db2user" | "db2.host" | 77 | "db2db" | "stdDatabaseName" - "jdbc:as400://ashost:66/asdb:user=asuser;password=PW;" | null | "as400" | "asuser" | "ashost" | 66 | "asdb" | null + "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 | "sap.host" | null | null | null - "jdbc:sap://sap.host" | stdProps | "sap" | "stdUserName" | "sap.host" | 9999 | null | "stdDatabaseName" - "jdbc:sap://sap.host:88/?databaseName=sapdb&user=sapuser&password=PW" | null | "sap" | "sapuser" | "sap.host" | 88 | null | "sapdb" + "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" | "infxuser" | "infxhost" | 99 | "infxdb"| null -// "jdbc:informix-direct://infxdb:999;user=infxuser;password=PW" | null | "informix-direct" | "infxuser" | "infxhost" | 999 | "infxdb"| null +// "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 + "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 + "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 + "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(format).user(user).instance(instance).db(db).host(host).port(port).build() + expected = new DBInfo.Builder().type(type).subtype(subtype).user(user).instance(instance).db(db).host(host).port(port).build() } } diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy index 995d6d71f9..9ecc358c19 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCInstrumentationTest.groovy @@ -26,23 +26,23 @@ class JDBCInstrumentationTest extends AgentTestRunner { @Shared private Map jdbcUrls = [ - "h2:mem" : "jdbc:h2:mem:$dbName", - "derby:memory": "jdbc:derby:memory:$dbName", - "hsqldb:mem" : "jdbc:hsqldb:mem:$dbName", + "h2" : "jdbc:h2:mem:$dbName", + "derby" : "jdbc:derby:memory:$dbName", + "hsqldb": "jdbc:hsqldb:mem:$dbName", ] @Shared private Map jdbcDriverClassNames = [ - "h2:mem" : "org.h2.Driver", - "derby:memory": "org.apache.derby.jdbc.EmbeddedDriver", - "hsqldb:mem" : "org.hsqldb.jdbc.JDBCDriver", + "h2" : "org.h2.Driver", + "derby" : "org.apache.derby.jdbc.EmbeddedDriver", + "hsqldb": "org.hsqldb.jdbc.JDBCDriver", ] @Shared private Map jdbcUserNames = [ - "h2:mem" : null, - "derby:memory": "APP", - "hsqldb:mem" : "SA", + "h2" : null, + "derby" : "APP", + "hsqldb": "SA", ] @Shared @@ -76,7 +76,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { def createTomcatDS(String dbType, String jdbcUrl) { DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource() - def jdbcUrlToSet = dbType == "derby:memory" ? jdbcUrl + ";create=true" : jdbcUrl + def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl ds.setUrl(jdbcUrlToSet) ds.setDriverClassName(jdbcDriverClassNames.get(dbType)) String username = jdbcUserNames.get(dbType) @@ -91,7 +91,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { def createHikariDS(String dbType, String jdbcUrl) { HikariConfig config = new HikariConfig() - def jdbcUrlToSet = dbType == "derby:memory" ? jdbcUrl + ";create=true" : jdbcUrl + def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl config.setJdbcUrl(jdbcUrlToSet) String username = jdbcUserNames.get(dbType) if (username != null) { @@ -109,7 +109,7 @@ class JDBCInstrumentationTest extends AgentTestRunner { def createC3P0DS(String dbType, String jdbcUrl) { DataSource ds = new ComboPooledDataSource() ds.setDriverClass(jdbcDriverClassNames.get(dbType)) - def jdbcUrlToSet = dbType == "derby:memory" ? jdbcUrl + ";create=true" : jdbcUrl + def jdbcUrlToSet = dbType == "derby" ? jdbcUrl + ";create=true" : jdbcUrl ds.setJdbcUrl(jdbcUrlToSet) String username = jdbcUserNames.get(dbType) if (username != null) { @@ -193,22 +193,22 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" - "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb:mem" | new JDBCDriver().connect(jdbcUrls.get("hsqldb:mem"), null) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" - "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), connectionProps) | null | "SELECT 3" - "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), connectionProps) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb:mem" | new JDBCDriver().connect(jdbcUrls.get("hsqldb:mem"), connectionProps) | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" - "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb:mem" | cpDatasources.get("tomcat").get("hsqldb:mem").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" - "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb:mem" | cpDatasources.get("hikari").get("hsqldb:mem").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" - "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "hsqldb:mem" | cpDatasources.get("c3p0").get("hsqldb:mem").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + driver | connection | username | query + "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" + "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" + "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" + "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" + "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" } @Unroll @@ -256,15 +256,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" - "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + driver | connection | username | query + "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" + "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll @@ -311,15 +311,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" - "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + driver | connection | username | query + "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" + "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll @@ -366,15 +366,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "SELECT 3" - "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "SELECT 3" - "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + driver | connection | username | query + "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" + "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "SELECT 3" + "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll @@ -421,19 +421,19 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb:mem" | new JDBCDriver().connect(jdbcUrls.get("hsqldb:mem"), null) | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "CREATE TABLE S_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "CREATE TABLE S_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb:mem" | cpDatasources.get("tomcat").get("hsqldb:mem").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "CREATE TABLE S_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "CREATE TABLE S_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb:mem" | cpDatasources.get("hikari").get("hsqldb:mem").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "CREATE TABLE S_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "CREATE TABLE S_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "hsqldb:mem" | cpDatasources.get("c3p0").get("hsqldb:mem").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + driver | connection | username | query + "h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "CREATE TABLE S_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null) | "APP" | "CREATE TABLE S_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb" | new JDBCDriver().connect(jdbcUrls.get("hsqldb"), null) | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "CREATE TABLE S_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb" | cpDatasources.get("tomcat").get("hsqldb").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "CREATE TABLE S_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb" | cpDatasources.get("hikari").get("hsqldb").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "CREATE TABLE S_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "CREATE TABLE S_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "hsqldb" | cpDatasources.get("c3p0").get("hsqldb").getConnection() | "SA" | "CREATE TABLE PUBLIC.S_HSQLDB_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" } @Unroll @@ -479,15 +479,15 @@ class JDBCInstrumentationTest extends AgentTestRunner { connection.close() where: - driver | connection | username | query - "h2:mem" | new Driver().connect(jdbcUrls.get("h2:mem"), null) | null | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | new EmbeddedDriver().connect(jdbcUrls.get("derby:memory"), null) | "APP" | "CREATE TABLE PS_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2:mem" | cpDatasources.get("tomcat").get("h2:mem").getConnection() | null | "CREATE TABLE PS_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | cpDatasources.get("tomcat").get("derby:memory").getConnection() | "APP" | "CREATE TABLE PS_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2:mem" | cpDatasources.get("hikari").get("h2:mem").getConnection() | null | "CREATE TABLE PS_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | cpDatasources.get("hikari").get("derby:memory").getConnection() | "APP" | "CREATE TABLE PS_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" - "h2:mem" | cpDatasources.get("c3p0").get("h2:mem").getConnection() | null | "CREATE TABLE PS_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" - "derby:memory" | cpDatasources.get("c3p0").get("derby:memory").getConnection() | "APP" | "CREATE TABLE PS_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + driver | connection | username | query + "h2" | new Driver().connect(jdbcUrls.get("h2"), null) | null | "CREATE TABLE PS_H2 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | new EmbeddedDriver().connect(jdbcUrls.get("derby"), null) | "APP" | "CREATE TABLE PS_DERBY (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2" | cpDatasources.get("tomcat").get("h2").getConnection() | null | "CREATE TABLE PS_H2_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | cpDatasources.get("tomcat").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_TOMCAT (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2" | cpDatasources.get("hikari").get("h2").getConnection() | null | "CREATE TABLE PS_H2_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | cpDatasources.get("hikari").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_HIKARI (id INTEGER not NULL, PRIMARY KEY ( id ))" + "h2" | cpDatasources.get("c3p0").get("h2").getConnection() | null | "CREATE TABLE PS_H2_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" + "derby" | cpDatasources.get("c3p0").get("derby").getConnection() | "APP" | "CREATE TABLE PS_DERBY_C3P0 (id INTEGER not NULL, PRIMARY KEY ( id ))" } @@ -558,17 +558,17 @@ class JDBCInstrumentationTest extends AgentTestRunner { } where: - prepareStatement | driver | driverClass | url | username | query - true | "h2:mem" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" - true | "derby:memory" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" - false | "h2:mem" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" - false | "derby:memory" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + prepareStatement | driver | driverClass | url | username | query + true | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" + true | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" + false | "h2" | new Driver() | "jdbc:h2:mem:" + dbName | null | "SELECT 3;" + false | "derby" | new EmbeddedDriver() | "jdbc:derby:memory:" + dbName + ";create=true" | "APP" | "SELECT 3 FROM SYSIBM.SYSDUMMY1" } @Unroll def "#connectionPoolName connections should be cached in case of wrapped connections"() { setup: - String dbType = "hsqldb:mem" + String dbType = "hsqldb" DataSource ds = createDS(connectionPoolName, dbType, jdbcUrls.get(dbType)) String query = "SELECT 3 FROM INFORMATION_SCHEMA.SYSTEM_USERS" int numQueries = 5 From 1a5a70650c20b0bdd7815b6fb37896fa77e468a0 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Mon, 10 Jun 2019 16:49:57 -0700 Subject: [PATCH 06/22] Fix SpringJpaTest --- .../src/test/groovy/SpringJpaTest.groovy | 55 +++++++------------ .../agent/test/asserts/SpanAssert.groovy | 7 +++ 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/dd-java-agent/instrumentation/hibernate/core-4.3/src/test/groovy/SpringJpaTest.groovy b/dd-java-agent/instrumentation/hibernate/core-4.3/src/test/groovy/SpringJpaTest.groovy index b936663e61..6a33ef9b3d 100644 --- a/dd-java-agent/instrumentation/hibernate/core-4.3/src/test/groovy/SpringJpaTest.groovy +++ b/dd-java-agent/instrumentation/hibernate/core-4.3/src/test/groovy/SpringJpaTest.groovy @@ -26,16 +26,12 @@ class SpringJpaTest extends AgentTestRunner { !repo.findAll().iterator().hasNext() assertTraces(1) { - trace(0, 2) { + trace(0, 1) { span(0) { serviceName "hsqldb" + resourceName "select customer0_.id as id1_0_, customer0_.firstName as firstNam2_0_, customer0_.lastName as lastName3_0_ from Customer customer0_" spanType "sql" } - span(1) { - serviceName "hsqldb" - spanType "sql" - childOf(span(0)) - } } } TEST_WRITER.clear() @@ -49,25 +45,22 @@ class SpringJpaTest extends AgentTestRunner { // Behavior changed in new version: def extraTrace = TEST_WRITER.size() == 2 assertTraces(extraTrace ? 2 : 1) { - trace(0, 2) { - span(0) { - serviceName "hsqldb" - spanType "sql" - } - span(1) { - serviceName "hsqldb" - spanType "sql" - childOf(span(0)) - } - } if (extraTrace) { - trace(1, 1) { + trace(0, 1) { span(0) { serviceName "hsqldb" + resourceName "call next value for hibernate_sequence" spanType "sql" } } } + trace(extraTrace ? 1 : 0, 1) { + span(0) { + serviceName "hsqldb" + resourceName ~/insert into Customer \(.*\) values \(.*, \?, \?\)/ + spanType "sql" + } + } } TEST_WRITER.clear() @@ -78,20 +71,17 @@ class SpringJpaTest extends AgentTestRunner { then: customer.id == savedId assertTraces(2) { - trace(0, 2) { + trace(0, 1) { span(0) { 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" } - span(1) { - serviceName "hsqldb" - spanType "sql" - childOf(span(0)) - } } trace(1, 1) { span(0) { serviceName "hsqldb" + resourceName "update Customer set firstName=?, lastName=? where id=?" spanType "sql" } } @@ -105,16 +95,12 @@ class SpringJpaTest extends AgentTestRunner { customer.id == savedId customer.firstName == "Bill" assertTraces(1) { - trace(0, 2) { + trace(0, 1) { span(0) { 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" } - span(1) { - serviceName "hsqldb" - spanType "sql" - childOf(span(0)) - } } } TEST_WRITER.clear() @@ -124,20 +110,17 @@ class SpringJpaTest extends AgentTestRunner { then: assertTraces(2) { - trace(0, 2) { + trace(0, 1) { span(0) { 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" } - span(1) { - serviceName "hsqldb" - spanType "sql" - childOf(span(0)) - } } trace(1, 1) { span(0) { serviceName "hsqldb" + resourceName "delete from Customer where id=?" spanType "sql" } } diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy index 319d6916e1..c5a9351885 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy @@ -4,6 +4,8 @@ import datadog.opentracing.DDSpan import groovy.transform.stc.ClosureParams import groovy.transform.stc.SimpleType +import java.util.regex.Pattern + import static TagsAssert.assertTags class SpanAssert { @@ -52,6 +54,11 @@ class SpanAssert { checked.operationName = true } + def resourceName(Pattern pattern) { + assert span.resourceName.matches(pattern) + checked.resourceName = true + } + def resourceName(String name) { assert span.resourceName == name checked.resourceName = true From 3c68c091018c85106972b032194e23a6a26030a6 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 11 Jun 2019 11:12:50 -0700 Subject: [PATCH 07/22] Review fixes. --- .../jdbc/DriverInstrumentation.java | 17 ++++++------ .../jdbc/JDBCConnectionUrlParser.java | 25 ++++++++--------- .../PreparedStatementInstrumentation.java | 27 +++++++++---------- .../jdbc/StatementInstrumentation.java | 27 +++++++++---------- .../groovy/JDBCConnectionUrlParserTest.groovy | 2 ++ 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java index 33c258284a..a5340ddbd3 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/DriverInstrumentation.java @@ -35,18 +35,17 @@ public final class DriverInstrumentation extends Instrumenter.Default { @Override public String[] helperClassNames() { - final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); - final List parserClasses = new ArrayList<>(parsers.length + 3); + final List helpers = new ArrayList<>(JDBCConnectionUrlParser.values().length + 4); - parserClasses.add(packageName + ".DBInfo"); - parserClasses.add(packageName + ".DBInfo$Builder"); - parserClasses.add(packageName + ".JDBCMaps"); - parserClasses.add(packageName + ".JDBCConnectionUrlParser"); + helpers.add(packageName + ".DBInfo"); + helpers.add(packageName + ".DBInfo$Builder"); + helpers.add(packageName + ".JDBCMaps"); + helpers.add(packageName + ".JDBCConnectionUrlParser"); - for (final JDBCConnectionUrlParser parser : parsers) { - parserClasses.add(parser.getClass().getName()); + for (final JDBCConnectionUrlParser parser : JDBCConnectionUrlParser.values()) { + helpers.add(parser.getClass().getName()); } - return parserClasses.toArray(new String[0]); + return helpers.toArray(new String[0]); } @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java index ba5cdf4a30..8f5124ad79 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCConnectionUrlParser.java @@ -618,36 +618,37 @@ public enum JDBCConnectionUrlParser { populateStandardProperties(builder, splitQuery(split[1], ";")); } - if (split[0].startsWith("memory:")) { + final String details = split[0]; + if (details.startsWith("memory:")) { builder.subtype("memory"); - final String urlInstance = split[0].substring("memory:".length()); + final String urlInstance = details.substring("memory:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } - } else if (split[0].startsWith("directory:")) { + } else if (details.startsWith("directory:")) { builder.subtype("directory"); - final String urlInstance = split[0].substring("directory:".length()); + final String urlInstance = details.substring("directory:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } - } else if (split[0].startsWith("classpath:")) { + } else if (details.startsWith("classpath:")) { builder.subtype("classpath"); - final String urlInstance = split[0].substring("classpath:".length()); + final String urlInstance = details.substring("classpath:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } - } else if (split[0].startsWith("jar:")) { + } else if (details.startsWith("jar:")) { builder.subtype("jar"); - final String urlInstance = split[0].substring("jar:".length()); + final String urlInstance = details.substring("jar:".length()); if (!urlInstance.isEmpty()) { instance = urlInstance; } - } else if (split[0].startsWith("//")) { + } else if (details.startsWith("//")) { builder.subtype("network"); if (dbInfo.getPort() == null) { builder.port(DEFAULT_PORT); } - String url = split[0].substring("//".length()); + String url = details.substring("//".length()); final int instanceLoc = url.indexOf("/"); if (instanceLoc >= 0) { instance = url.substring(instanceLoc + 1); @@ -666,7 +667,7 @@ public enum JDBCConnectionUrlParser { } } else { builder.subtype("directory"); - final String urlInstance = split[0]; + final String urlInstance = details; if (!urlInstance.isEmpty()) { instance = urlInstance; } @@ -701,7 +702,7 @@ public enum JDBCConnectionUrlParser { if (connectionUrl == null) { return DEFAULT; } - // Make this easer and ignore case. + // Make this easier and ignore case. connectionUrl = connectionUrl.toLowerCase(); if (!connectionUrl.startsWith("jdbc:")) { diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java index 1b4b0d7da6..2242387c1d 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/PreparedStatementInstrumentation.java @@ -42,24 +42,23 @@ public final class PreparedStatementInstrumentation extends Instrumenter.Default @Override public String[] helperClassNames() { - final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); - final List parserClasses = new ArrayList<>(parsers.length + 8); + final List helpers = new ArrayList<>(JDBCConnectionUrlParser.values().length + 9); - parserClasses.add(packageName + ".DBInfo"); - parserClasses.add(packageName + ".DBInfo$Builder"); - parserClasses.add(packageName + ".JDBCUtils"); - parserClasses.add(packageName + ".JDBCMaps"); - parserClasses.add(packageName + ".JDBCConnectionUrlParser"); + helpers.add(packageName + ".DBInfo"); + helpers.add(packageName + ".DBInfo$Builder"); + helpers.add(packageName + ".JDBCUtils"); + helpers.add(packageName + ".JDBCMaps"); + helpers.add(packageName + ".JDBCConnectionUrlParser"); - parserClasses.add("datadog.trace.agent.decorator.BaseDecorator"); - parserClasses.add("datadog.trace.agent.decorator.ClientDecorator"); - parserClasses.add("datadog.trace.agent.decorator.DatabaseClientDecorator"); - parserClasses.add(packageName + ".JDBCDecorator"); + 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 : parsers) { - parserClasses.add(parser.getClass().getName()); + for (final JDBCConnectionUrlParser parser : JDBCConnectionUrlParser.values()) { + helpers.add(parser.getClass().getName()); } - return parserClasses.toArray(new String[0]); + return helpers.toArray(new String[0]); } @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java index 9d513319e9..5c0fb81177 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/StatementInstrumentation.java @@ -42,24 +42,23 @@ public final class StatementInstrumentation extends Instrumenter.Default { @Override public String[] helperClassNames() { - final JDBCConnectionUrlParser[] parsers = JDBCConnectionUrlParser.values(); - final List parserClasses = new ArrayList<>(parsers.length + 8); + final List helpers = new ArrayList<>(JDBCConnectionUrlParser.values().length + 9); - parserClasses.add(packageName + ".DBInfo"); - parserClasses.add(packageName + ".DBInfo$Builder"); - parserClasses.add(packageName + ".JDBCUtils"); - parserClasses.add(packageName + ".JDBCMaps"); - parserClasses.add(packageName + ".JDBCConnectionUrlParser"); + helpers.add(packageName + ".DBInfo"); + helpers.add(packageName + ".DBInfo$Builder"); + helpers.add(packageName + ".JDBCUtils"); + helpers.add(packageName + ".JDBCMaps"); + helpers.add(packageName + ".JDBCConnectionUrlParser"); - parserClasses.add("datadog.trace.agent.decorator.BaseDecorator"); - parserClasses.add("datadog.trace.agent.decorator.ClientDecorator"); - parserClasses.add("datadog.trace.agent.decorator.DatabaseClientDecorator"); - parserClasses.add(packageName + ".JDBCDecorator"); + 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 : parsers) { - parserClasses.add(parser.getClass().getName()); + for (final JDBCConnectionUrlParser parser : JDBCConnectionUrlParser.values()) { + helpers.add(parser.getClass().getName()); } - return parserClasses.toArray(new String[0]); + return helpers.toArray(new String[0]); } @Override diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy index 3e70935b54..e0fb31854a 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/JDBCConnectionUrlParserTest.groovy @@ -30,6 +30,8 @@ class JDBCConnectionUrlParserTest extends Specification { url | _ null | _ "" | _ + "jdbc:" | _ + "jdbc::" | _ "bogus:string" | _ } From 742fc2ff8b10bea5e8da9094aede690a785f3d1a Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 11 Jun 2019 19:03:26 -0700 Subject: [PATCH 08/22] Update Jackson Databind to 2.9.9 Due to new security vulerablility. --- gradle/dependencies.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index f7b1741862..00d9a61366 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -7,7 +7,7 @@ ext { slf4j : "1.7.25", guava : "20.0", // Last version to support Java 7 - jackson : "2.9.8", // https://nvd.nist.gov/vuln/detail/CVE-2018-1000873 + jackson : "2.9.9", // https://nvd.nist.gov/vuln/detail/CVE-2019-12086 spock : "1.3-groovy-$spockGroovyVer", groovy : groovyVer, From a2a9bec8a8a26f7577f84268255dee1ec80a1791 Mon Sep 17 00:00:00 2001 From: Nikolay Martynov Date: Thu, 13 Jun 2019 10:47:24 -0400 Subject: [PATCH 09/22] Enable '**' support in logs collection scripts It is disabled by default now --- .circleci/collect_reports.sh | 2 ++ .circleci/collect_results.sh | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.circleci/collect_reports.sh b/.circleci/collect_reports.sh index e813bbfb61..9cb27f8e94 100755 --- a/.circleci/collect_reports.sh +++ b/.circleci/collect_reports.sh @@ -4,6 +4,8 @@ # This folder will be saved by circleci and available after test runs. set -e +#Enable '**' support +shopt -s globstar REPORTS_DIR=./reports mkdir -p $REPORTS_DIR >/dev/null 2>&1 diff --git a/.circleci/collect_results.sh b/.circleci/collect_results.sh index 13072caf04..bde5432216 100755 --- a/.circleci/collect_results.sh +++ b/.circleci/collect_results.sh @@ -4,6 +4,8 @@ # This folder will be saved by circleci and available after test runs. set -e +#Enable '**' support +shopt -s globstar TEST_RESULTS_DIR=./results mkdir -p $TEST_RESULTS_DIR >/dev/null 2>&1 From f5f3386f79cae899c40ee981fdb7b40266832fc6 Mon Sep 17 00:00:00 2001 From: Nikolay Martynov Date: Thu, 13 Jun 2019 11:51:10 -0400 Subject: [PATCH 10/22] Make Akka client test more stable We already have a hack to wait for client span to close after the request because it is closed on separate thread. This patch extends that hack to handle cases when original request throws an exception. --- .../test/groovy/AkkaHttpClientInstrumentationTest.groovy | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/instrumentation/akka-http-10.0/src/test/groovy/AkkaHttpClientInstrumentationTest.groovy b/dd-java-agent/instrumentation/akka-http-10.0/src/test/groovy/AkkaHttpClientInstrumentationTest.groovy index ff13ad89ca..3ae9770f32 100644 --- a/dd-java-agent/instrumentation/akka-http-10.0/src/test/groovy/AkkaHttpClientInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/akka-http-10.0/src/test/groovy/AkkaHttpClientInstrumentationTest.groovy @@ -23,8 +23,12 @@ class AkkaHttpClientInstrumentationTest extends HttpClientTest Date: Wed, 12 Jun 2019 13:31:24 -0400 Subject: [PATCH 11/22] Collect more debugging info --- .circleci/collect_reports.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/collect_reports.sh b/.circleci/collect_reports.sh index 9cb27f8e94..3e7891bd24 100755 --- a/.circleci/collect_reports.sh +++ b/.circleci/collect_reports.sh @@ -19,6 +19,9 @@ function save_reports () { report_path=$REPORTS_DIR/$project_to_save mkdir -p $report_path cp -r workspace/$project_to_save/build/reports/* $report_path/ + cp -r workspace/$project_to_save/build/core* $report_path/ || true + cp -r workspace/$project_to_save/build/hs_err_pid* $report_path/ || true + cp -r workspace/$project_to_save/build/replay* $report_path/ || true } shopt -s globstar From 16709c095484765fe228ea34c11cbce192f1d49c Mon Sep 17 00:00:00 2001 From: Nikolay Martynov Date: Thu, 13 Jun 2019 12:41:47 -0400 Subject: [PATCH 12/22] Preserve .gradle in workspace --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b3fb07018..5972ceaa00 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,6 +38,7 @@ jobs: - persist_to_workspace: root: . paths: + - .gradle - workspace - save_cache: From ca57aade8c710eefbfa76c7775e0234b24c98eb6 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 12 Jun 2019 09:34:20 -0700 Subject: [PATCH 13/22] Run latestDepTests in a separate job --- .circleci/config.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 4b3fb07018..dfeae6a06c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -93,7 +93,13 @@ jobs: <<: *default_test_job environment: # We are building on Java8, this is our default JVM so no need to set more homes - - TEST_TASK: test latestDepTest jacocoTestReport jacocoTestCoverageVerification + - TEST_TASK: test jacocoTestReport jacocoTestCoverageVerification + + test_latest8: + <<: *default_test_job + environment: + # We are building on Java8, this is our default JVM so no need to set more homes + - TEST_TASK: latestDepTest test_ibm8: <<: *default_test_job @@ -249,6 +255,12 @@ workflows: filters: tags: only: /.*/ + - test_latest8: + requires: + - build + filters: + tags: + only: /.*/ - test_ibm8: requires: - build @@ -305,6 +317,7 @@ workflows: requires: - test_7 - test_8 + - test_latest8 - test_ibm8 - test_9 - test_10 @@ -322,6 +335,7 @@ workflows: requires: - test_7 - test_8 + - test_latest8 - test_ibm8 - test_9 - test_10 From d6719ed5cd7c6c5c132fa802e94ffaf924980952 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Tue, 11 Jun 2019 10:54:17 -0700 Subject: [PATCH 14/22] Replace Mongo tests with updated tests. --- dd-java-agent/dd-java-agent.gradle | 7 - .../mongo-3.1/mongo-3.1.gradle | 5 +- .../groovy/MongoClientDecoratorTest.groovy | 45 --- .../src/test/groovy/MongoClientTest.groovy | 263 ++++++++++++++++++ .../mongo/MongoClientInstrumentationTest.java | 65 ----- .../mongo-async-3.3/mongo-async-3.3.gradle | 15 + .../test/groovy/MongoAsyncClientTest.groovy | 263 ++++++++++++++++++ .../MongoAsyncClientInstrumentationTest.java | 122 -------- .../MongoClientInstrumentationTest.java | 117 -------- 9 files changed, 543 insertions(+), 359 deletions(-) delete mode 100644 dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy create mode 100644 dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy delete mode 100644 dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java create mode 100644 dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy delete mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java delete mode 100644 dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java diff --git a/dd-java-agent/dd-java-agent.gradle b/dd-java-agent/dd-java-agent.gradle index 81ccc6669a..f408630093 100644 --- a/dd-java-agent/dd-java-agent.gradle +++ b/dd-java-agent/dd-java-agent.gradle @@ -97,13 +97,6 @@ dependencies { testCompile deps.opentracingMock testCompile deps.testLogging testCompile deps.guava - testCompile group: 'org.assertj', name: 'assertj-core', version: '2.9.+' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' - - testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.4.2' - testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.4.2' - // run embedded mongodb for integration testing - testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' } tasks.withType(Test).configureEach { diff --git a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle b/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle index 5f1a0d2a0a..a8e4eff200 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle +++ b/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle @@ -28,9 +28,8 @@ dependencies { implementation deps.autoservice testCompile project(':dd-java-agent:testing') - - testCompile group: 'org.assertj', name: 'assertj-core', version: '2.9.+' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' + + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0' latestDepTestCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '+' diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy b/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy deleted file mode 100644 index 4ddc61f661..0000000000 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientDecoratorTest.groovy +++ /dev/null @@ -1,45 +0,0 @@ -import datadog.trace.api.DDTags -import io.opentracing.Span -import io.opentracing.tag.Tags -import org.bson.BsonArray -import org.bson.BsonDocument -import org.bson.BsonString -import spock.lang.Shared -import spock.lang.Specification - -import static datadog.trace.instrumentation.mongo.MongoClientDecorator.DECORATE - -class MongoClientDecoratorTest extends Specification { - - @Shared - def query1, query2 - - def setupSpec() { - query1 = new BsonDocument("find", new BsonString("show")) - query1.put("stuff", new BsonString("secret")) - - - query2 = new BsonDocument("insert", new BsonString("table")) - def nestedDoc = new BsonDocument("count", new BsonString("show")) - nestedDoc.put("id", new BsonString("secret")) - query2.put("docs", new BsonArray(Arrays.asList(new BsonString("secret"), nestedDoc))) - } - - def "test query scrubbing"() { - setup: - def span = Mock(Span) - // all "secret" strings should be scrubbed out of these queries - - when: - DECORATE.onStatement(span, query) - - then: - 1 * span.setTag(Tags.DB_STATEMENT.key, expected) - 1 * span.setTag(DDTags.RESOURCE_NAME, expected) - 0 * _ - - where: - query << [query1, query2] - expected = query.toString().replaceAll("secret", "?") - } -} diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy new file mode 100644 index 0000000000..4f32eaba58 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy @@ -0,0 +1,263 @@ +import com.mongodb.MongoClient +import com.mongodb.MongoClientOptions +import com.mongodb.MongoTimeoutException +import com.mongodb.ServerAddress +import com.mongodb.client.MongoCollection +import com.mongodb.client.MongoDatabase +import datadog.opentracing.DDSpan +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.asserts.TraceAssert +import datadog.trace.agent.test.utils.PortUtils +import datadog.trace.api.DDSpanTypes +import de.flapdoodle.embed.mongo.MongodExecutable +import de.flapdoodle.embed.mongo.MongodProcess +import de.flapdoodle.embed.mongo.MongodStarter +import de.flapdoodle.embed.mongo.config.IMongodConfig +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder +import de.flapdoodle.embed.mongo.config.Net +import de.flapdoodle.embed.mongo.distribution.Version +import de.flapdoodle.embed.process.runtime.Network +import io.opentracing.tag.Tags +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import spock.lang.Shared + +import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace + +class MongoClientTest extends AgentTestRunner { + + @Shared + MongoClient client + @Shared + int port = PortUtils.randomOpenPort() + @Shared + MongodExecutable mongodExe + @Shared + MongodProcess mongod + + def setup() throws Exception { + final MongodStarter starter = MongodStarter.getDefaultInstance() + final IMongodConfig mongodConfig = + new MongodConfigBuilder() + .version(Version.Main.PRODUCTION) + .net(new Net("localhost", port, Network.localhostIsIPv6())) + .build() + + mongodExe = starter.prepare(mongodConfig) + mongod = mongodExe.start() + + client = new MongoClient("localhost", port) + } + + def cleanup() throws Exception { + client?.close() + client = null + mongod?.stop() + mongod = null + mongodExe?.stop() + mongodExe = null + } + + def "test create collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + db.createCollection(collectionName) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test get collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + int count = db.getCollection(collectionName).count() + + then: + count == 0 + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test insert"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + collection.insertOne(new Document("password", "SECRET")) + + then: + collection.count() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test update"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + def coll = db.getCollection(collectionName) + coll.insertOne(new Document("password", "OLDPW")) + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = collection.updateOne( + new BsonDocument("password", new BsonString("OLDPW")), + new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW")))) + + then: + result.modifiedCount == 1 + collection.count() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test delete"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + def coll = db.getCollection(collectionName) + coll.insertOne(new Document("password", "SECRET")) + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = collection.deleteOne(new BsonDocument("password", new BsonString("SECRET"))) + + then: + result.deletedCount == 1 + collection.count() == 0 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test error"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + collection.updateOne(new BsonDocument(), new BsonDocument()) + + then: + thrown(IllegalArgumentException) + // Unfortunately not caught by our instrumentation. + assertTraces(0) {} + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test client failure"() { + setup: + def options = MongoClientOptions.builder().serverSelectionTimeout(10).build() + def client = new MongoClient(new ServerAddress("localhost", UNUSABLE_PORT), [], options) + + when: + MongoDatabase db = client.getDatabase(dbName) + db.createCollection(collectionName) + + then: + thrown(MongoTimeoutException) + // Unfortunately not caught by our instrumentation. + assertTraces(0) {} + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def mongoSpan(TraceAssert trace, int index, String statement, Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + serviceName "mongo" + operationName "mongo.query" + resourceName statement + spanType DDSpanTypes.MONGO + if (parentSpan == null) { + parent() + } else { + childOf((DDSpan) parentSpan) + } + tags { + "$Tags.COMPONENT.key" "java-mongo" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$Tags.DB_INSTANCE.key" "test_db" + "$Tags.DB_STATEMENT.key" statement + "$Tags.DB_TYPE.key" "mongo" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" port + defaultTags() + } + } + } +} diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java b/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java deleted file mode 100644 index e3dd177876..0000000000 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentationTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package datadog.trace.instrumentation.mongo; - -import static org.assertj.core.api.Java6Assertions.assertThat; - -import com.mongodb.ServerAddress; -import com.mongodb.connection.ClusterId; -import com.mongodb.connection.ConnectionDescription; -import com.mongodb.connection.ServerId; -import com.mongodb.event.CommandStartedEvent; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.api.DDSpanTypes; -import io.opentracing.tag.Tags; -import java.util.Arrays; -import java.util.List; -import org.bson.BsonArray; -import org.bson.BsonDocument; -import org.bson.BsonString; -import org.junit.Test; - -public class MongoClientInstrumentationTest { - - private static ConnectionDescription makeConnection() { - return new ConnectionDescription(new ServerId(new ClusterId(), new ServerAddress())); - } - - @Test - public void mongoSpan() { - final CommandStartedEvent cmd = - new CommandStartedEvent(1, makeConnection(), "databasename", "query", new BsonDocument()); - - final DDSpan span = new DDTracer().buildSpan("foo").start(); - MongoClientDecorator.DECORATE.afterStart(span); - MongoClientDecorator.DECORATE.onStatement(span, cmd.getCommand()); - - assertThat(span.context().getSpanType()).isEqualTo("mongodb"); - assertThat(span.context().getResourceName()) - .isEqualTo(span.context().getTags().get("db.statement")); - assertThat(span.getSpanType()).isEqualTo(DDSpanTypes.MONGO); - } - - @Test - public void queryScrubbing() { - // all "secret" strings should be scrubbed out of these queries - final BsonDocument query1 = new BsonDocument("find", new BsonString("show")); - query1.put("stuff", new BsonString("secret")); - final BsonDocument query2 = new BsonDocument("insert", new BsonString("table")); - final BsonDocument query2_1 = new BsonDocument("count", new BsonString("show")); - query2_1.put("id", new BsonString("secret")); - query2.put("docs", new BsonArray(Arrays.asList(new BsonString("secret"), query2_1))); - final List queries = Arrays.asList(query1, query2); - for (final BsonDocument query : queries) { - final CommandStartedEvent cmd = - new CommandStartedEvent(1, makeConnection(), "databasename", "query", query); - - final DDSpan span = new DDTracer().buildSpan("foo").start(); - MongoClientDecorator.DECORATE.afterStart(span); - MongoClientDecorator.DECORATE.onStatement(span, cmd.getCommand()); - - assertThat(span.getSpanType()).isEqualTo(DDSpanTypes.MONGO); - assertThat(span.getTags().get(Tags.DB_STATEMENT.getKey())) - .isEqualTo(query.toString().replaceAll("secret", "?")); - } - } -} diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle b/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle index df7028ecb5..03579283ed 100644 --- a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle +++ b/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle @@ -10,6 +10,14 @@ muzzle { apply from: "${rootDir}/gradle/java.gradle" +apply plugin: 'org.unbroken-dome.test-sets' + +testSets { + latestDepTest { + dirName = 'test' + } +} + dependencies { // use mongo listener compile(project(':dd-java-agent:instrumentation:mongo-3.1')) { @@ -24,4 +32,11 @@ dependencies { compile deps.opentracing annotationProcessor deps.autoservice implementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' + + testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' + latestDepTestCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '+' } diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy new file mode 100644 index 0000000000..cb3c7debb0 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -0,0 +1,263 @@ +import com.mongodb.async.SingleResultCallback +import com.mongodb.async.client.MongoClient +import com.mongodb.async.client.MongoClients +import com.mongodb.async.client.MongoCollection +import com.mongodb.async.client.MongoDatabase +import com.mongodb.client.result.DeleteResult +import com.mongodb.client.result.UpdateResult +import datadog.opentracing.DDSpan +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.asserts.TraceAssert +import datadog.trace.agent.test.utils.PortUtils +import datadog.trace.api.DDSpanTypes +import de.flapdoodle.embed.mongo.MongodExecutable +import de.flapdoodle.embed.mongo.MongodProcess +import de.flapdoodle.embed.mongo.MongodStarter +import de.flapdoodle.embed.mongo.config.IMongodConfig +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder +import de.flapdoodle.embed.mongo.config.Net +import de.flapdoodle.embed.mongo.distribution.Version +import de.flapdoodle.embed.process.runtime.Network +import io.opentracing.tag.Tags +import org.bson.BsonDocument +import org.bson.BsonString +import org.bson.Document +import spock.lang.Shared +import spock.lang.Timeout + +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CountDownLatch + +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace + +@Timeout(10) +class MongoAsyncClientTest extends AgentTestRunner { + + @Shared + MongoClient client + @Shared + int port = PortUtils.randomOpenPort() + @Shared + MongodExecutable mongodExe + @Shared + MongodProcess mongod + + def setup() throws Exception { + final MongodStarter starter = MongodStarter.getDefaultInstance() + final IMongodConfig mongodConfig = + new MongodConfigBuilder() + .version(Version.Main.PRODUCTION) + .net(new Net("localhost", port, Network.localhostIsIPv6())) + .build() + + mongodExe = starter.prepare(mongodConfig) + mongod = mongodExe.start() + + client = MongoClients.create("mongodb://localhost:$port") + } + + def cleanup() throws Exception { + client?.close() + client = null + mongod?.stop() + mongod = null + mongodExe?.stop() + mongodExe = null + } + + def "test create collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + db.createCollection(collectionName, toCallback {}) + + then: + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test get collection"() { + setup: + MongoDatabase db = client.getDatabase(dbName) + + when: + def count = new CompletableFuture() + db.getCollection(collectionName).count toCallback { count.complete(it) } + + then: + count.get() == 0 + assertTraces(1) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test insert"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + return db.getCollection(collectionName) + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def count = new CompletableFuture() + collection.insertOne(new Document("password", "SECRET"), toCallback { + collection.count toCallback { count.complete(it) } + }) + + then: + count.get() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test update"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + def coll = db.getCollection(collectionName) + def latch2 = new CountDownLatch(1) + coll.insertOne(new Document("password", "OLDPW"), toCallback { latch2.countDown() }) + latch2.await() + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = new CompletableFuture() + def count = new CompletableFuture() + collection.updateOne( + new BsonDocument("password", new BsonString("OLDPW")), + new BsonDocument('$set', new BsonDocument("password", new BsonString("NEWPW"))), toCallback { + result.complete(it) + collection.count toCallback { count.complete(it) } + }) + + then: + result.get().modifiedCount == 1 + count.get() == 1 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + def "test delete"() { + setup: + MongoCollection collection = runUnderTrace("setup") { + MongoDatabase db = client.getDatabase(dbName) + def latch1 = new CountDownLatch(1) + db.createCollection(collectionName, toCallback { latch1.countDown() }) + latch1.await() + def coll = db.getCollection(collectionName) + def latch2 = new CountDownLatch(1) + coll.insertOne(new Document("password", "SECRET"), toCallback { latch2.countDown() }) + latch2.await() + return coll + } + TEST_WRITER.waitForTraces(1) + TEST_WRITER.clear() + + when: + def result = new CompletableFuture() + def count = new CompletableFuture() + collection.deleteOne(new BsonDocument("password", new BsonString("SECRET")), toCallback { + result.complete(it) + collection.count toCallback { count.complete(it) } + }) + + then: + result.get().deletedCount == 1 + count.get() == 0 + assertTraces(2) { + trace(0, 1) { + mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + } + trace(1, 1) { + mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + } + } + + where: + dbName = "test_db" + collectionName = "testCollection" + } + + SingleResultCallback toCallback(Closure closure) { + return new SingleResultCallback() { + @Override + void onResult(Object result, Throwable t) { + if (t) { + closure.call(t) + } else { + closure.call(result) + } + } + } + } + + def mongoSpan(TraceAssert trace, int index, String statement, Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + serviceName "mongo" + operationName "mongo.query" + resourceName statement + spanType DDSpanTypes.MONGO + if (parentSpan == null) { + parent() + } else { + childOf((DDSpan) parentSpan) + } + tags { + "$Tags.COMPONENT.key" "java-mongo" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$Tags.DB_INSTANCE.key" "test_db" + "$Tags.DB_STATEMENT.key" statement + "$Tags.DB_TYPE.key" "mongo" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" port + defaultTags() + } + } + } +} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java deleted file mode 100644 index 86d2f58c9d..0000000000 --- a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoAsyncClientInstrumentationTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package datadog.trace.agent.integration; - -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_DB_NAME; -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_HOST; -import static datadog.trace.agent.integration.MongoClientInstrumentationTest.MONGO_PORT; - -import com.mongodb.async.SingleResultCallback; -import com.mongodb.async.client.MongoClient; -import com.mongodb.async.client.MongoClients; -import com.mongodb.async.client.MongoDatabase; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.agent.test.IntegrationTestUtils; -import datadog.trace.common.writer.ListWriter; -import io.opentracing.tag.Tags; -import java.util.concurrent.atomic.AtomicBoolean; -import org.bson.Document; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class MongoAsyncClientInstrumentationTest { - private static MongoClient client; - private static final ListWriter writer = new ListWriter(); - private static final DDTracer tracer = new DDTracer(writer); - - @BeforeClass - public static void setup() throws Exception { - IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer); - MongoClientInstrumentationTest.startLocalMongo(); - // Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before - // going to tests - writer.clear(); - - client = MongoClients.create("mongodb://" + MONGO_HOST + ":" + MONGO_PORT); - } - - @AfterClass - public static void destroy() throws Exception { - if (null != client) { - client.close(); - client = null; - } - MongoClientInstrumentationTest.stopLocalMongo(); - } - - @Test - public void asyncClientHasListener() { - Assert.assertEquals(1, client.getSettings().getCommandListeners().size()); - Assert.assertEquals( - "TracingCommandListener", - client.getSettings().getCommandListeners().get(0).getClass().getSimpleName()); - } - - @Test - public void insertOperation() throws Exception { - final MongoDatabase db = client.getDatabase(MONGO_DB_NAME); - final String collectionName = "asyncCollection"; - final AtomicBoolean done = new AtomicBoolean(false); - - db.createCollection( - collectionName, - new SingleResultCallback() { - @Override - public void onResult(final Void result, final Throwable t) { - done.set(true); - } - }); - while (!done.get()) { - Thread.sleep(1); - } - - db.getCollection(collectionName) - .insertOne( - new Document("foo", "bar"), - new SingleResultCallback() { - @Override - public void onResult(final Void result, final Throwable t) { - done.set(true); - } - }); - while (!done.get()) { - Thread.sleep(1); - } - - done.set(false); - db.getCollection(collectionName) - .count( - new SingleResultCallback() { - @Override - public void onResult(final Long result, final Throwable t) { - Assert.assertEquals(1, result.longValue()); - done.set(true); - } - }); - - while (!done.get()) { - Thread.sleep(1); - } - - // the final trace may still be reporting to the ListWriter, - // but we're only testing the first trace. - Assert.assertTrue(writer.size() >= 1); - - final String createCollectionQuery = - "{ \"create\" : \"asyncCollection\", \"autoIndexId\" : \"?\", \"capped\" : \"?\" }"; - final DDSpan trace0 = writer.get(0).get(0); - Assert.assertEquals("mongo.query", trace0.getOperationName()); - Assert.assertEquals(createCollectionQuery, trace0.getResourceName()); - Assert.assertEquals("mongodb", trace0.getType()); - Assert.assertEquals("mongo", trace0.getServiceName()); - - Assert.assertEquals("java-mongo", trace0.getTags().get(Tags.COMPONENT.getKey())); - Assert.assertEquals(createCollectionQuery, trace0.getTags().get(Tags.DB_STATEMENT.getKey())); - Assert.assertEquals(MONGO_DB_NAME, trace0.getTags().get(Tags.DB_INSTANCE.getKey())); - Assert.assertEquals(MONGO_HOST, trace0.getTags().get(Tags.PEER_HOSTNAME.getKey())); - Assert.assertEquals("127.0.0.1", trace0.getTags().get(Tags.PEER_HOST_IPV4.getKey())); - Assert.assertEquals(MONGO_PORT, trace0.getTags().get(Tags.PEER_PORT.getKey())); - Assert.assertEquals("mongo", trace0.getTags().get(Tags.DB_TYPE.getKey())); - } -} diff --git a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java b/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java deleted file mode 100644 index 9942c6e535..0000000000 --- a/dd-java-agent/src/test/java/datadog/trace/agent/integration/MongoClientInstrumentationTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package datadog.trace.agent.integration; - -import com.mongodb.MongoClient; -import com.mongodb.client.MongoCollection; -import com.mongodb.client.MongoDatabase; -import datadog.opentracing.DDSpan; -import datadog.opentracing.DDTracer; -import datadog.trace.agent.test.IntegrationTestUtils; -import datadog.trace.common.writer.ListWriter; -import de.flapdoodle.embed.mongo.MongodExecutable; -import de.flapdoodle.embed.mongo.MongodProcess; -import de.flapdoodle.embed.mongo.MongodStarter; -import de.flapdoodle.embed.mongo.config.IMongodConfig; -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; -import de.flapdoodle.embed.mongo.config.Net; -import de.flapdoodle.embed.mongo.distribution.Version; -import de.flapdoodle.embed.process.runtime.Network; -import io.opentracing.tag.Tags; -import java.util.concurrent.TimeoutException; -import org.bson.Document; -import org.junit.AfterClass; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class MongoClientInstrumentationTest { - public static final String MONGO_DB_NAME = "embedded"; - public static final String MONGO_HOST = "localhost"; - public static final int MONGO_PORT = 12345; - private static MongodExecutable mongodExe; - private static MongodProcess mongod; - - private static MongoClient client; - private static final ListWriter writer = new ListWriter(); - private static final DDTracer tracer = new DDTracer(writer); - - public static void startLocalMongo() throws Exception { - final MongodStarter starter = MongodStarter.getDefaultInstance(); - final IMongodConfig mongodConfig = - new MongodConfigBuilder() - .version(Version.Main.PRODUCTION) - .net(new Net(MONGO_HOST, MONGO_PORT, Network.localhostIsIPv6())) - .build(); - - mongodExe = starter.prepare(mongodConfig); - mongod = mongodExe.start(); - } - - public static void stopLocalMongo() throws Exception { - if (null != mongod) { - mongod.stop(); - mongod = null; - } - if (null != mongodExe) { - mongodExe.stop(); - mongodExe = null; - } - } - - @BeforeClass - public static void setup() throws Exception { - IntegrationTestUtils.registerOrReplaceGlobalTracer(tracer); - startLocalMongo(); - // Embeded Mongo uses HttpUrlConnection to download things so we have to clear traces before - // going to tests - writer.clear(); - - client = new MongoClient(MONGO_HOST, MONGO_PORT); - } - - @AfterClass - public static void destroy() throws Exception { - if (null != client) { - client.close(); - client = null; - } - stopLocalMongo(); - } - - @Test - public void syncClientHasListener() { - Assert.assertEquals(1, client.getMongoClientOptions().getCommandListeners().size()); - Assert.assertEquals( - "TracingCommandListener", - client.getMongoClientOptions().getCommandListeners().get(0).getClass().getSimpleName()); - } - - @Test - public void insertOperation() throws TimeoutException, InterruptedException { - final MongoDatabase db = client.getDatabase(MONGO_DB_NAME); - final String collectionName = "testCollection"; - db.createCollection(collectionName); - final MongoCollection collection = db.getCollection(collectionName); - - collection.insertOne(new Document("foo", "bar")); - - Assert.assertEquals(1, collection.count()); - - Assert.assertEquals(3, writer.size()); - - final String createCollectionQuery = - "{ \"create\" : \"testCollection\", \"autoIndexId\" : \"?\", \"capped\" : \"?\" }"; - final DDSpan trace0 = writer.get(0).get(0); - Assert.assertEquals("mongo.query", trace0.getOperationName()); - Assert.assertEquals(createCollectionQuery, trace0.getResourceName()); - Assert.assertEquals("mongodb", trace0.getType()); - Assert.assertEquals("mongo", trace0.getServiceName()); - - Assert.assertEquals("java-mongo", trace0.getTags().get(Tags.COMPONENT.getKey())); - Assert.assertEquals(createCollectionQuery, trace0.getTags().get(Tags.DB_STATEMENT.getKey())); - Assert.assertEquals(MONGO_DB_NAME, trace0.getTags().get(Tags.DB_INSTANCE.getKey())); - Assert.assertEquals(MONGO_HOST, trace0.getTags().get(Tags.PEER_HOSTNAME.getKey())); - Assert.assertEquals("127.0.0.1", trace0.getTags().get(Tags.PEER_HOST_IPV4.getKey())); - Assert.assertEquals(MONGO_PORT, trace0.getTags().get(Tags.PEER_PORT.getKey())); - Assert.assertEquals("mongo", trace0.getTags().get(Tags.DB_TYPE.getKey())); - } -} From d26edd6c17a86340fc979edf5291a4f2afff445d Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 12 Jun 2019 14:46:11 -0700 Subject: [PATCH 15/22] Ensure gradle runs base mongo test first Otherwise there is a race condition between the projects initializing the mongo instance. --- .../driver-3.1/driver-3.1.gradle} | 2 +- .../mongo/MongoClientDecorator.java | 0 .../mongo/MongoClientInstrumentation.java | 0 .../mongo/TracingCommandListener.java | 0 .../src/test/groovy/MongoClientTest.groovy | 32 +------ .../driver-async-3.3/driver-async-3.3.gradle} | 5 +- .../MongoAsyncClientInstrumentation.java | 0 .../test/groovy/MongoAsyncClientTest.groovy | 86 ++++++++++--------- .../instrumentation/mongo/mongo.gradle | 28 ++++++ .../src/test/groovy/MongoBaseTest.groovy | 54 ++++++++++++ .../src/test/java/NoOpInstrumentation.java | 12 +++ .../agent/test/asserts/SpanAssert.groovy | 5 ++ settings.gradle | 5 +- 13 files changed, 150 insertions(+), 79 deletions(-) rename dd-java-agent/instrumentation/{mongo-3.1/mongo-3.1.gradle => mongo/driver-3.1/driver-3.1.gradle} (91%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java (100%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java (100%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java (100%) rename dd-java-agent/instrumentation/{mongo-3.1 => mongo/driver-3.1}/src/test/groovy/MongoClientTest.groovy (85%) rename dd-java-agent/instrumentation/{mongo-async-3.3/mongo-async-3.3.gradle => mongo/driver-async-3.3/driver-async-3.3.gradle} (86%) rename dd-java-agent/instrumentation/{mongo-async-3.3 => mongo/driver-async-3.3}/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java (100%) rename dd-java-agent/instrumentation/{mongo-async-3.3 => mongo/driver-async-3.3}/src/test/groovy/MongoAsyncClientTest.groovy (67%) create mode 100644 dd-java-agent/instrumentation/mongo/mongo.gradle create mode 100644 dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy create mode 100644 dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle similarity index 91% rename from dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle rename to dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle index a8e4eff200..f93bacc3e2 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/mongo-3.1.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle @@ -28,7 +28,7 @@ dependencies { implementation deps.autoservice testCompile project(':dd-java-agent:testing') - + testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0' diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientInstrumentation.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/TracingCommandListener.java diff --git a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy similarity index 85% rename from dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy rename to dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy index 4f32eaba58..ef7d9fb116 100644 --- a/dd-java-agent/instrumentation/mongo-3.1/src/test/groovy/MongoClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy @@ -5,18 +5,8 @@ import com.mongodb.ServerAddress import com.mongodb.client.MongoCollection import com.mongodb.client.MongoDatabase import datadog.opentracing.DDSpan -import datadog.trace.agent.test.AgentTestRunner import datadog.trace.agent.test.asserts.TraceAssert -import datadog.trace.agent.test.utils.PortUtils import datadog.trace.api.DDSpanTypes -import de.flapdoodle.embed.mongo.MongodExecutable -import de.flapdoodle.embed.mongo.MongodProcess -import de.flapdoodle.embed.mongo.MongodStarter -import de.flapdoodle.embed.mongo.config.IMongodConfig -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder -import de.flapdoodle.embed.mongo.config.Net -import de.flapdoodle.embed.mongo.distribution.Version -import de.flapdoodle.embed.process.runtime.Network import io.opentracing.tag.Tags import org.bson.BsonDocument import org.bson.BsonString @@ -26,38 +16,18 @@ import spock.lang.Shared import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace -class MongoClientTest extends AgentTestRunner { +abstract class MongoClientTest extends MongoBaseTest { @Shared MongoClient client - @Shared - int port = PortUtils.randomOpenPort() - @Shared - MongodExecutable mongodExe - @Shared - MongodProcess mongod def setup() throws Exception { - final MongodStarter starter = MongodStarter.getDefaultInstance() - final IMongodConfig mongodConfig = - new MongodConfigBuilder() - .version(Version.Main.PRODUCTION) - .net(new Net("localhost", port, Network.localhostIsIPv6())) - .build() - - mongodExe = starter.prepare(mongodConfig) - mongod = mongodExe.start() - client = new MongoClient("localhost", port) } def cleanup() throws Exception { client?.close() client = null - mongod?.stop() - mongod = null - mongodExe?.stop() - mongodExe = null } def "test create collection"() { diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle similarity index 86% rename from dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle rename to dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle index 03579283ed..36b7fbe845 100644 --- a/dd-java-agent/instrumentation/mongo-async-3.3/mongo-async-3.3.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle @@ -20,10 +20,9 @@ testSets { dependencies { // use mongo listener - compile(project(':dd-java-agent:instrumentation:mongo-3.1')) { + compile(project(':dd-java-agent:instrumentation:mongo:driver-3.1')) { transitive = false } - compileOnly group: 'org.mongodb', name: 'mongo-java-driver', version: '3.3.0' compileOnly group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' compile project(':dd-java-agent:agent-tooling') @@ -34,7 +33,7 @@ dependencies { implementation deps.autoservice testCompile project(':dd-java-agent:testing') - + testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java similarity index 100% rename from dd-java-agent/instrumentation/mongo-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java rename to dd-java-agent/instrumentation/mongo/driver-async-3.3/src/main/java/datadog/trace/instrumentation/mongo/MongoAsyncClientInstrumentation.java diff --git a/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy similarity index 67% rename from dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy rename to dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy index cb3c7debb0..2caa1f7044 100644 --- a/dd-java-agent/instrumentation/mongo-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -6,18 +6,8 @@ import com.mongodb.async.client.MongoDatabase import com.mongodb.client.result.DeleteResult import com.mongodb.client.result.UpdateResult import datadog.opentracing.DDSpan -import datadog.trace.agent.test.AgentTestRunner import datadog.trace.agent.test.asserts.TraceAssert -import datadog.trace.agent.test.utils.PortUtils import datadog.trace.api.DDSpanTypes -import de.flapdoodle.embed.mongo.MongodExecutable -import de.flapdoodle.embed.mongo.MongodProcess -import de.flapdoodle.embed.mongo.MongodStarter -import de.flapdoodle.embed.mongo.config.IMongodConfig -import de.flapdoodle.embed.mongo.config.MongodConfigBuilder -import de.flapdoodle.embed.mongo.config.Net -import de.flapdoodle.embed.mongo.distribution.Version -import de.flapdoodle.embed.process.runtime.Network import io.opentracing.tag.Tags import org.bson.BsonDocument import org.bson.BsonString @@ -31,38 +21,18 @@ import java.util.concurrent.CountDownLatch import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace @Timeout(10) -class MongoAsyncClientTest extends AgentTestRunner { +class MongoAsyncClientTest extends MongoBaseTest { @Shared MongoClient client - @Shared - int port = PortUtils.randomOpenPort() - @Shared - MongodExecutable mongodExe - @Shared - MongodProcess mongod def setup() throws Exception { - final MongodStarter starter = MongodStarter.getDefaultInstance() - final IMongodConfig mongodConfig = - new MongodConfigBuilder() - .version(Version.Main.PRODUCTION) - .net(new Net("localhost", port, Network.localhostIsIPv6())) - .build() - - mongodExe = starter.prepare(mongodConfig) - mongod = mongodExe.start() - client = MongoClients.create("mongodb://localhost:$port") } def cleanup() throws Exception { client?.close() client = null - mongod?.stop() - mongod = null - mongodExe?.stop() - mongodExe = null } def "test create collection"() { @@ -75,7 +45,11 @@ class MongoAsyncClientTest extends AgentTestRunner { then: assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + mongoSpan(it, 0) { + assert it == "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }" || + it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -96,7 +70,11 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 0 assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -127,10 +105,18 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + mongoSpan(it, 0) { + assert it == "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }" || + it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}" + true + } } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -170,10 +156,18 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + mongoSpan(it, 0) { + assert it == "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }" || + it == "{\"update\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"updates\": [{\"q\": {\"password\": \"?\"}, \"u\": {\"\$set\": {\"password\": \"?\"}}}]}" + true + } } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -211,10 +205,18 @@ class MongoAsyncClientTest extends AgentTestRunner { count.get() == 0 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + mongoSpan(it, 0) { + assert it == "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }" || + it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}" + true + } } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0) { + assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" + true + } } } @@ -236,11 +238,11 @@ class MongoAsyncClientTest extends AgentTestRunner { } } - def mongoSpan(TraceAssert trace, int index, String statement, Object parentSpan = null, Throwable exception = null) { + def mongoSpan(TraceAssert trace, int index, Closure statementEval, Object parentSpan = null, Throwable exception = null) { trace.span(index) { serviceName "mongo" operationName "mongo.query" - resourceName statement + resourceName statementEval spanType DDSpanTypes.MONGO if (parentSpan == null) { parent() @@ -251,7 +253,7 @@ class MongoAsyncClientTest extends AgentTestRunner { "$Tags.COMPONENT.key" "java-mongo" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$Tags.DB_INSTANCE.key" "test_db" - "$Tags.DB_STATEMENT.key" statement + "$Tags.DB_STATEMENT.key" statementEval "$Tags.DB_TYPE.key" "mongo" "$Tags.PEER_HOSTNAME.key" "localhost" "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" diff --git a/dd-java-agent/instrumentation/mongo/mongo.gradle b/dd-java-agent/instrumentation/mongo/mongo.gradle new file mode 100644 index 0000000000..437c738bcd --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/mongo.gradle @@ -0,0 +1,28 @@ +apply from: "${rootDir}/gradle/java.gradle" + +dependencies { + testAnnotationProcessor deps.autoservice + testImplementation deps.autoservice + + testCompile project(':dd-java-agent:testing') + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' +} + +// Forcing strict test execution order (no parallel execution) to ensure proper mongo executable initialization. +List testTasks = [] +tasks.withType(Test) { Test testTask -> + testTasks.each { + testTask.shouldRunAfter(it) + } + testTasks.add(testTask) +} +subprojects { + afterEvaluate { + tasks.withType(Test) { Test testTask -> + testTasks.each { + testTask.shouldRunAfter(it) + } + testTasks.add(testTask) + } + } +} diff --git a/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy new file mode 100644 index 0000000000..0fede79b21 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy @@ -0,0 +1,54 @@ +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.utils.PortUtils +import de.flapdoodle.embed.mongo.MongodExecutable +import de.flapdoodle.embed.mongo.MongodProcess +import de.flapdoodle.embed.mongo.MongodStarter +import de.flapdoodle.embed.mongo.config.IMongodConfig +import de.flapdoodle.embed.mongo.config.MongodConfigBuilder +import de.flapdoodle.embed.mongo.config.Net +import de.flapdoodle.embed.mongo.distribution.Version +import de.flapdoodle.embed.process.runtime.Network +import spock.lang.Shared + +/** + * Testing needs to be in a centralized project. + * If tests in multiple different projects are using embedded mongo, + * they downloader is at risk of a race condition. + */ +class MongoBaseTest extends AgentTestRunner { + // https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo#executable-collision + private static final MongodStarter starter = MongodStarter.getDefaultInstance() + + @Shared + int port = PortUtils.randomOpenPort() + @Shared + MongodExecutable mongodExe + @Shared + MongodProcess mongod + + def setup() throws Exception { + final IMongodConfig mongodConfig = + new MongodConfigBuilder() + .version(Version.Main.PRODUCTION) + .net(new Net("localhost", port, Network.localhostIsIPv6())) + .build() + + mongodExe = starter.prepare(mongodConfig) + mongod = mongodExe.start() + } + + def cleanup() throws Exception { + mongod?.stop() + mongod = null + mongodExe?.stop() + mongodExe = null + } + + def "test port open"() { + when: + new Socket("localhost", port) + + then: + noExceptionThrown() + } +} diff --git a/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java b/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java new file mode 100644 index 0000000000..d830b1adf3 --- /dev/null +++ b/dd-java-agent/instrumentation/mongo/src/test/java/NoOpInstrumentation.java @@ -0,0 +1,12 @@ +import com.google.auto.service.AutoService; +import datadog.trace.agent.tooling.Instrumenter; +import net.bytebuddy.agent.builder.AgentBuilder; + +@AutoService(Instrumenter.class) +public class NoOpInstrumentation implements Instrumenter { + + @Override + public AgentBuilder instrument(final AgentBuilder agentBuilder) { + return agentBuilder; + } +} diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy index c5a9351885..eae9c17550 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/asserts/SpanAssert.groovy @@ -64,6 +64,11 @@ class SpanAssert { checked.resourceName = true } + def resourceName(Closure eval) { + assert eval(span.resourceName) + checked.resourceName = true + } + def resourceNameContains(String... resourceNameParts) { assertSpanNameContains(span.resourceName, resourceNameParts) checked.resourceName = true diff --git a/settings.gradle b/settings.gradle index d31e0b382d..0cd07e4c11 100644 --- a/settings.gradle +++ b/settings.gradle @@ -66,8 +66,9 @@ include ':dd-java-agent:instrumentation:jsp-2.3' include ':dd-java-agent:instrumentation:kafka-clients-0.11' include ':dd-java-agent:instrumentation:kafka-streams-0.11' include ':dd-java-agent:instrumentation:lettuce-5' -include ':dd-java-agent:instrumentation:mongo-3.1' -include ':dd-java-agent:instrumentation:mongo-async-3.3' +include ':dd-java-agent:instrumentation:mongo' +include ':dd-java-agent:instrumentation:mongo:driver-3.1' +include ':dd-java-agent:instrumentation:mongo:driver-async-3.3' include ':dd-java-agent:instrumentation:netty-4.0' include ':dd-java-agent:instrumentation:netty-4.1' include ':dd-java-agent:instrumentation:okhttp-3' From 25305444d4a6e5d9340056630c7816b01e33289c Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Wed, 12 Jun 2019 17:40:17 -0700 Subject: [PATCH 16/22] Set minimum version and fix var name. --- .../instrumentation/mongo/driver-3.1/driver-3.1.gradle | 2 +- .../mongo/driver-async-3.3/driver-async-3.3.gradle | 8 +++++++- dd-java-agent/instrumentation/mongo/mongo.gradle | 2 +- .../mongo/src/test/groovy/MongoBaseTest.groovy | 4 ++-- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle index f93bacc3e2..747c30ad60 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/driver-3.1.gradle @@ -29,7 +29,7 @@ dependencies { testCompile project(':dd-java-agent:testing') testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output - testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' testCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '3.1.0' latestDepTestCompile group: 'org.mongodb', name: 'mongo-java-driver', version: '+' diff --git a/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle index 36b7fbe845..0461926bfb 100644 --- a/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/driver-async-3.3.gradle @@ -1,3 +1,9 @@ +// Set properties before any plugins get loaded +ext { + // Since we're using CompletableFutures in the test... + minJavaVersionForTests = JavaVersion.VERSION_1_8 +} + muzzle { pass { group = "org.mongodb" @@ -34,7 +40,7 @@ dependencies { testCompile project(':dd-java-agent:testing') testCompile project(':dd-java-agent:instrumentation:mongo').sourceSets.test.output - testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' testCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '3.3.0' latestDepTestCompile group: 'org.mongodb', name: 'mongodb-driver-async', version: '+' diff --git a/dd-java-agent/instrumentation/mongo/mongo.gradle b/dd-java-agent/instrumentation/mongo/mongo.gradle index 437c738bcd..f91b66b0cc 100644 --- a/dd-java-agent/instrumentation/mongo/mongo.gradle +++ b/dd-java-agent/instrumentation/mongo/mongo.gradle @@ -5,7 +5,7 @@ dependencies { testImplementation deps.autoservice testCompile project(':dd-java-agent:testing') - testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '2.2.0' + testCompile group: 'de.flapdoodle.embed', name: 'de.flapdoodle.embed.mongo', version: '1.50.5' } // Forcing strict test execution order (no parallel execution) to ensure proper mongo executable initialization. diff --git a/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy index 0fede79b21..c945733e35 100644 --- a/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy +++ b/dd-java-agent/instrumentation/mongo/src/test/groovy/MongoBaseTest.groovy @@ -17,7 +17,7 @@ import spock.lang.Shared */ class MongoBaseTest extends AgentTestRunner { // https://github.com/flapdoodle-oss/de.flapdoodle.embed.mongo#executable-collision - private static final MongodStarter starter = MongodStarter.getDefaultInstance() + private static final MongodStarter STARTER = MongodStarter.getDefaultInstance() @Shared int port = PortUtils.randomOpenPort() @@ -33,7 +33,7 @@ class MongoBaseTest extends AgentTestRunner { .net(new Net("localhost", port, Network.localhostIsIPv6())) .build() - mongodExe = starter.prepare(mongodConfig) + mongodExe = STARTER.prepare(mongodConfig) mongod = mongodExe.start() } From 75b626a84d03e47d0b025e14d8e5ddd0265e3b4d Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 13 Jun 2019 10:59:36 -0700 Subject: [PATCH 17/22] fix tests --- .../src/test/groovy/MongoAsyncClientTest.groovy | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy index 2caa1f7044..aa0b80f1e1 100644 --- a/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-async-3.3/src/test/groovy/MongoAsyncClientTest.groovy @@ -46,7 +46,7 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(1) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }" || + assert it.replaceAll(" ", "") == "{\"create\":\"$collectionName\",\"capped\":\"?\"}" || it == "{\"create\": \"$collectionName\", \"capped\": \"?\", \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -71,7 +71,7 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(1) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -106,14 +106,14 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(2) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }" || + assert it.replaceAll(" ", "") == "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}" || it == "{\"insert\": \"$collectionName\", \"ordered\": \"?\", \"\$db\": \"?\", \"documents\": [{\"_id\": \"?\", \"password\": \"?\"}]}" true } } trace(1, 1) { mongoSpan(it, 0) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -157,14 +157,14 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(2) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }" || + assert it.replaceAll(" ", "") == "{\"update\":\"?\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}" || it == "{\"update\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"updates\": [{\"q\": {\"password\": \"?\"}, \"u\": {\"\$set\": {\"password\": \"?\"}}}]}" true } } trace(1, 1) { mongoSpan(it, 0) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } @@ -206,14 +206,14 @@ class MongoAsyncClientTest extends MongoBaseTest { assertTraces(2) { trace(0, 1) { mongoSpan(it, 0) { - assert it == "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }" || + assert it.replaceAll(" ", "") == "{\"delete\":\"?\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}" || it == "{\"delete\": \"?\", \"ordered\": \"?\", \"\$db\": \"?\", \"deletes\": [{\"q\": {\"password\": \"?\"}, \"limit\": \"?\"}]}" true } } trace(1, 1) { mongoSpan(it, 0) { - assert it == "{ \"count\" : \"$collectionName\", \"query\" : { } }" || + assert it.replaceAll(" ", "") == "{\"count\":\"$collectionName\",\"query\":{}}" || it == "{\"count\": \"$collectionName\", \"query\": {}, \"\$db\": \"?\", \"\$readPreference\": {\"mode\": \"?\"}}" true } From 3be6868981a20c802fbfdea5b131bdf9549a7d6b Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 13 Jun 2019 11:58:42 -0700 Subject: [PATCH 18/22] Update Mongo instance name and remove memcache instance name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For mongo, use description if set, fallback to db name. Memcache doesn’t have a good “instance name” that would work as service name, so clear it out for now. --- .../mongo/MongoClientDecorator.java | 34 ++++++++++------ .../src/test/groovy/MongoClientTest.groovy | 30 ++++++++++++-- .../test/groovy/MongoAsyncClientTest.groovy | 39 +++++++++++++++++-- .../spymemcached/MemcacheClientDecorator.java | 2 +- .../spymemcached/SpymemcachedTest.groovy | 1 - 5 files changed, 85 insertions(+), 21 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java index 08474ce55b..a5d4782cdc 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/main/java/datadog/trace/instrumentation/mongo/MongoClientDecorator.java @@ -1,5 +1,9 @@ package datadog.trace.instrumentation.mongo; +import com.mongodb.connection.ClusterId; +import com.mongodb.connection.ConnectionDescription; +import com.mongodb.connection.ConnectionId; +import com.mongodb.connection.ServerId; import com.mongodb.event.CommandStartedEvent; import datadog.trace.agent.decorator.DatabaseClientDecorator; import datadog.trace.api.DDSpanTypes; @@ -48,19 +52,25 @@ public class MongoClientDecorator extends DatabaseClientDecorator statementEval, Object parentSpan = null, Throwable exception = null) { + def mongoSpan(TraceAssert trace, int index, Closure statementEval, String instance = "some-description", Object parentSpan = null, Throwable exception = null) { trace.span(index) { serviceName "mongo" operationName "mongo.query" @@ -252,7 +285,7 @@ class MongoAsyncClientTest extends MongoBaseTest { tags { "$Tags.COMPONENT.key" "java-mongo" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT - "$Tags.DB_INSTANCE.key" "test_db" + "$Tags.DB_INSTANCE.key" instance "$Tags.DB_STATEMENT.key" statementEval "$Tags.DB_TYPE.key" "mongo" "$Tags.PEER_HOSTNAME.key" "localhost" diff --git a/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java b/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java index f5729f94c6..cbb51e6784 100644 --- a/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java +++ b/dd-java-agent/instrumentation/spymemcached-2.12/src/main/java/datadog/trace/instrumentation/spymemcached/MemcacheClientDecorator.java @@ -41,7 +41,7 @@ public class MemcacheClientDecorator extends DatabaseClientDecorator Date: Thu, 13 Jun 2019 14:54:41 -0700 Subject: [PATCH 19/22] Fix latest test --- .../src/test/groovy/MongoClientTest.groovy | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy index 6ecb6ef3ef..66ded8bb60 100644 --- a/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy +++ b/dd-java-agent/instrumentation/mongo/driver-3.1/src/test/groovy/MongoClientTest.groovy @@ -43,7 +43,7 @@ class MongoClientTest extends MongoBaseTest { then: assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }") + mongoSpan(it, 0, "{\"create\":\"$collectionName\",\"capped\":\"?\"}") } } @@ -62,7 +62,7 @@ class MongoClientTest extends MongoBaseTest { then: assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"create\" : \"$collectionName\", \"capped\" : \"?\" }", dbName) + mongoSpan(it, 0, "{\"create\":\"$collectionName\",\"capped\":\"?\"}", dbName) } } @@ -82,7 +82,7 @@ class MongoClientTest extends MongoBaseTest { count == 0 assertTraces(1) { trace(0, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -108,10 +108,10 @@ class MongoClientTest extends MongoBaseTest { collection.count() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"insert\" : \"$collectionName\", \"ordered\" : \"?\", \"documents\" : [{ \"_id\" : \"?\", \"password\" : \"?\" }] }") + mongoSpan(it, 0, "{\"insert\":\"$collectionName\",\"ordered\":\"?\",\"documents\":[{\"_id\":\"?\",\"password\":\"?\"}]}") } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -142,10 +142,10 @@ class MongoClientTest extends MongoBaseTest { collection.count() == 1 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"update\" : \"?\", \"ordered\" : \"?\", \"updates\" : [{ \"q\" : { \"password\" : \"?\" }, \"u\" : { \"\$set\" : { \"password\" : \"?\" } } }] }") + mongoSpan(it, 0, "{\"update\":\"?\",\"ordered\":\"?\",\"updates\":[{\"q\":{\"password\":\"?\"},\"u\":{\"\$set\":{\"password\":\"?\"}}}]}") } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -174,10 +174,10 @@ class MongoClientTest extends MongoBaseTest { collection.count() == 0 assertTraces(2) { trace(0, 1) { - mongoSpan(it, 0, "{ \"delete\" : \"?\", \"ordered\" : \"?\", \"deletes\" : [{ \"q\" : { \"password\" : \"?\" }, \"limit\" : \"?\" }] }") + mongoSpan(it, 0, "{\"delete\":\"?\",\"ordered\":\"?\",\"deletes\":[{\"q\":{\"password\":\"?\"},\"limit\":\"?\"}]}") } trace(1, 1) { - mongoSpan(it, 0, "{ \"count\" : \"$collectionName\", \"query\" : { } }") + mongoSpan(it, 0, "{\"count\":\"$collectionName\",\"query\":{}}") } } @@ -232,7 +232,10 @@ class MongoClientTest extends MongoBaseTest { trace.span(index) { serviceName "mongo" operationName "mongo.query" - resourceName statement + resourceName { + assert it.replace(" ", "") == statement + return true + } spanType DDSpanTypes.MONGO if (parentSpan == null) { parent() @@ -243,7 +246,9 @@ class MongoClientTest extends MongoBaseTest { "$Tags.COMPONENT.key" "java-mongo" "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT "$Tags.DB_INSTANCE.key" instance - "$Tags.DB_STATEMENT.key" statement + "$Tags.DB_STATEMENT.key" { + it.replace(" ", "") == statement + } "$Tags.DB_TYPE.key" "mongo" "$Tags.PEER_HOSTNAME.key" "localhost" "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" From 4c5793bddff01e09f6e9c168e2ff0cf4eef5feef Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 13 Jun 2019 14:28:16 -0700 Subject: [PATCH 20/22] Add `dd.trace.db.client.split-by-instance` Config --- .../decorator/DatabaseClientDecorator.java | 8 +++++++- .../DatabaseClientDecoratorTest.groovy | 20 +++++++++++++------ .../decorator/HttpClientDecoratorTest.groovy | 10 +++++----- .../decorator/OrmClientDecoratorTest.groovy | 3 --- .../main/java/datadog/trace/api/Config.java | 11 ++++++++++ .../datadog/trace/api/ConfigTest.groovy | 10 ++++++++++ 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/decorator/DatabaseClientDecorator.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/decorator/DatabaseClientDecorator.java index edbdda6c4a..14995cd19e 100644 --- a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/decorator/DatabaseClientDecorator.java +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/decorator/DatabaseClientDecorator.java @@ -1,5 +1,7 @@ package datadog.trace.agent.decorator; +import datadog.trace.api.Config; +import datadog.trace.api.DDTags; import io.opentracing.Span; import io.opentracing.tag.Tags; @@ -29,7 +31,11 @@ public abstract class DatabaseClientDecorator extends ClientDecorato assert span != null; if (connection != null) { Tags.DB_USER.set(span, dbUser(connection)); - Tags.DB_INSTANCE.set(span, dbInstance(connection)); + final String instanceName = dbInstance(connection); + Tags.DB_INSTANCE.set(span, instanceName); + if (instanceName != null && Config.get().isDbClientSplitByInstance()) { + span.setTag(DDTags.SERVICE_NAME, instanceName); + } } return span; } diff --git a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/DatabaseClientDecoratorTest.groovy b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/DatabaseClientDecoratorTest.groovy index e1bae7ae91..b37c5c8680 100644 --- a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/DatabaseClientDecoratorTest.groovy +++ b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/DatabaseClientDecoratorTest.groovy @@ -1,9 +1,12 @@ package datadog.trace.agent.decorator +import datadog.trace.api.Config import datadog.trace.api.DDTags import io.opentracing.Span import io.opentracing.tag.Tags +import static datadog.trace.agent.test.utils.ConfigUtils.withConfigOverride + class DatabaseClientDecoratorTest extends ClientDecoratorTest { def span = Mock(Span) @@ -35,21 +38,26 @@ class DatabaseClientDecoratorTest extends ClientDecoratorTest { def decorator = newDecorator() when: - decorator.onConnection(span, session) + withConfigOverride(Config.DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "$renameService") { + decorator.onConnection(span, session) + } then: if (session) { 1 * span.setTag(Tags.DB_USER.key, session.user) 1 * span.setTag(Tags.DB_INSTANCE.key, session.instance) + if (renameService && session.instance) { + 1 * span.setTag(DDTags.SERVICE_NAME, session.instance) + } } 0 * _ where: - session | _ - null | _ - [user: "test-user"] | _ - [instance: "test-instance"] | _ - [user: "test-user", instance: "test-instance"] | _ + renameService | session + false | null + true | [user: "test-user"] + false | [instance: "test-instance"] + true | [user: "test-user", instance: "test-instance"] } def "test onStatement"() { diff --git a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpClientDecoratorTest.groovy b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpClientDecoratorTest.groovy index f9ed2a9189..afab046789 100644 --- a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpClientDecoratorTest.groovy +++ b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/HttpClientDecoratorTest.groovy @@ -26,12 +26,12 @@ class HttpClientDecoratorTest extends ClientDecoratorTest { then: if (req) { - 1 * span.setTag(Tags.HTTP_METHOD.key, "test-method") - 1 * span.setTag(Tags.HTTP_URL.key, "$testUrl") - 1 * span.setTag(Tags.PEER_HOSTNAME.key, "test-host") - 1 * span.setTag(Tags.PEER_PORT.key, 555) + 1 * span.setTag(Tags.HTTP_METHOD.key, req.method) + 1 * span.setTag(Tags.HTTP_URL.key, "$req.url") + 1 * span.setTag(Tags.PEER_HOSTNAME.key, req.host) + 1 * span.setTag(Tags.PEER_PORT.key, req.port) if (renameService) { - 1 * span.setTag(DDTags.SERVICE_NAME, "test-host") + 1 * span.setTag(DDTags.SERVICE_NAME, req.host) } } 0 * _ diff --git a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/OrmClientDecoratorTest.groovy b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/OrmClientDecoratorTest.groovy index 4e99cb5fc0..810b51519e 100644 --- a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/OrmClientDecoratorTest.groovy +++ b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/decorator/OrmClientDecoratorTest.groovy @@ -1,12 +1,9 @@ package datadog.trace.agent.decorator import datadog.trace.api.DDTags -import io.opentracing.Span class OrmClientDecoratorTest extends DatabaseClientDecoratorTest { - def span = Mock(Span) - def "test onOperation #testName"() { setup: decorator = newDecorator({ e -> entityName }) diff --git a/dd-trace-api/src/main/java/datadog/trace/api/Config.java b/dd-trace-api/src/main/java/datadog/trace/api/Config.java index fafb539372..9b960b4450 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/Config.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/Config.java @@ -63,6 +63,7 @@ public class Config { public static final String HTTP_SERVER_TAG_QUERY_STRING = "http.server.tag.query-string"; public static final String HTTP_CLIENT_TAG_QUERY_STRING = "http.client.tag.query-string"; public static final String HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN = "trace.http.client.split-by-domain"; + public static final String DB_CLIENT_HOST_SPLIT_BY_INSTANCE = "trace.db.client.split-by-instance"; public static final String PARTIAL_FLUSH_MIN_SPANS = "trace.partial.flush.min.spans"; public static final String RUNTIME_CONTEXT_FIELD_INJECTION = "trace.runtime.context.field.injection"; @@ -107,6 +108,7 @@ public class Config { private static final boolean DEFAULT_HTTP_SERVER_TAG_QUERY_STRING = false; private static final boolean DEFAULT_HTTP_CLIENT_TAG_QUERY_STRING = false; private static final boolean DEFAULT_HTTP_CLIENT_SPLIT_BY_DOMAIN = false; + private static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE = false; private static final int DEFAULT_PARTIAL_FLUSH_MIN_SPANS = 1000; private static final String DEFAULT_PROPAGATION_STYLE_EXTRACT = PropagationStyle.DATADOG.name(); private static final String DEFAULT_PROPAGATION_STYLE_INJECT = PropagationStyle.DATADOG.name(); @@ -160,6 +162,7 @@ public class Config { @Getter private final boolean httpServerTagQueryString; @Getter private final boolean httpClientTagQueryString; @Getter private final boolean httpClientSplitByDomain; + @Getter private final boolean dbClientSplitByInstance; @Getter private final Integer partialFlushMinSpans; @Getter private final boolean runtimeContextFieldInjection; @Getter private final Set propagationStylesToExtract; @@ -238,6 +241,10 @@ public class Config { getBooleanSettingFromEnvironment( HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, DEFAULT_HTTP_CLIENT_SPLIT_BY_DOMAIN); + dbClientSplitByInstance = + getBooleanSettingFromEnvironment( + DB_CLIENT_HOST_SPLIT_BY_INSTANCE, DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE); + partialFlushMinSpans = getIntegerSettingFromEnvironment(PARTIAL_FLUSH_MIN_SPANS, DEFAULT_PARTIAL_FLUSH_MIN_SPANS); @@ -342,6 +349,10 @@ public class Config { getPropertyBooleanValue( properties, HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, parent.httpClientSplitByDomain); + dbClientSplitByInstance = + getPropertyBooleanValue( + properties, DB_CLIENT_HOST_SPLIT_BY_INSTANCE, parent.dbClientSplitByInstance); + partialFlushMinSpans = getPropertyIntegerValue(properties, PARTIAL_FLUSH_MIN_SPANS, parent.partialFlushMinSpans); diff --git a/dd-trace-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy b/dd-trace-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy index 069f1a98ed..afc7ca1941 100644 --- a/dd-trace-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy +++ b/dd-trace-api/src/test/groovy/datadog/trace/api/ConfigTest.groovy @@ -8,6 +8,7 @@ import spock.lang.Specification import static datadog.trace.api.Config.AGENT_HOST import static datadog.trace.api.Config.AGENT_PORT_LEGACY import static datadog.trace.api.Config.AGENT_UNIX_DOMAIN_SOCKET +import static datadog.trace.api.Config.DB_CLIENT_HOST_SPLIT_BY_INSTANCE import static datadog.trace.api.Config.DEFAULT_JMX_FETCH_STATSD_PORT import static datadog.trace.api.Config.GLOBAL_TAGS import static datadog.trace.api.Config.HEADER_TAGS @@ -79,6 +80,7 @@ class ConfigTest extends Specification { config.httpServerErrorStatuses == (500..599).toSet() config.httpClientErrorStatuses == (400..499).toSet() config.httpClientSplitByDomain == false + config.dbClientSplitByInstance == false config.partialFlushMinSpans == 1000 config.reportHostName == false config.runtimeContextFieldInjection == true @@ -120,6 +122,7 @@ class ConfigTest extends Specification { prop.setProperty(HTTP_SERVER_ERROR_STATUSES, "123-456,457,124-125,122") prop.setProperty(HTTP_CLIENT_ERROR_STATUSES, "111") prop.setProperty(HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "true") + prop.setProperty(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "true") prop.setProperty(PARTIAL_FLUSH_MIN_SPANS, "15") prop.setProperty(TRACE_REPORT_HOSTNAME, "true") prop.setProperty(RUNTIME_CONTEXT_FIELD_INJECTION, "false") @@ -151,6 +154,7 @@ class ConfigTest extends Specification { config.httpServerErrorStatuses == (122..457).toSet() config.httpClientErrorStatuses == (111..111).toSet() config.httpClientSplitByDomain == true + config.dbClientSplitByInstance == true config.partialFlushMinSpans == 15 config.reportHostName == true config.runtimeContextFieldInjection == false @@ -183,6 +187,7 @@ class ConfigTest extends Specification { System.setProperty(PREFIX + HTTP_SERVER_ERROR_STATUSES, "123-456,457,124-125,122") System.setProperty(PREFIX + HTTP_CLIENT_ERROR_STATUSES, "111") System.setProperty(PREFIX + HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "true") + System.setProperty(PREFIX + DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "true") System.setProperty(PREFIX + PARTIAL_FLUSH_MIN_SPANS, "25") System.setProperty(PREFIX + TRACE_REPORT_HOSTNAME, "true") System.setProperty(PREFIX + RUNTIME_CONTEXT_FIELD_INJECTION, "false") @@ -214,6 +219,7 @@ class ConfigTest extends Specification { config.httpServerErrorStatuses == (122..457).toSet() config.httpClientErrorStatuses == (111..111).toSet() config.httpClientSplitByDomain == true + config.dbClientSplitByInstance == true config.partialFlushMinSpans == 25 config.reportHostName == true config.runtimeContextFieldInjection == false @@ -287,6 +293,7 @@ class ConfigTest extends Specification { System.setProperty(PREFIX + HTTP_SERVER_ERROR_STATUSES, "1111") System.setProperty(PREFIX + HTTP_CLIENT_ERROR_STATUSES, "1:1") System.setProperty(PREFIX + HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "invalid") + System.setProperty(PREFIX + DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "invalid") System.setProperty(PREFIX + PROPAGATION_STYLE_EXTRACT, "some garbage") System.setProperty(PREFIX + PROPAGATION_STYLE_INJECT, " ") @@ -307,6 +314,7 @@ class ConfigTest extends Specification { config.httpServerErrorStatuses == (500..599).toSet() config.httpClientErrorStatuses == (400..499).toSet() config.httpClientSplitByDomain == false + config.dbClientSplitByInstance == false config.propagationStylesToExtract.toList() == [Config.PropagationStyle.DATADOG] config.propagationStylesToInject.toList() == [Config.PropagationStyle.DATADOG] } @@ -372,6 +380,7 @@ class ConfigTest extends Specification { properties.setProperty(HTTP_SERVER_ERROR_STATUSES, "123-456,457,124-125,122") properties.setProperty(HTTP_CLIENT_ERROR_STATUSES, "111") properties.setProperty(HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "true") + properties.setProperty(DB_CLIENT_HOST_SPLIT_BY_INSTANCE, "true") properties.setProperty(PARTIAL_FLUSH_MIN_SPANS, "15") properties.setProperty(PROPAGATION_STYLE_EXTRACT, "B3 Datadog") properties.setProperty(PROPAGATION_STYLE_INJECT, "Datadog B3") @@ -400,6 +409,7 @@ class ConfigTest extends Specification { config.httpServerErrorStatuses == (122..457).toSet() config.httpClientErrorStatuses == (111..111).toSet() config.httpClientSplitByDomain == true + config.dbClientSplitByInstance == true config.partialFlushMinSpans == 15 config.propagationStylesToExtract.toList() == [Config.PropagationStyle.B3, Config.PropagationStyle.DATADOG] config.propagationStylesToInject.toList() == [Config.PropagationStyle.DATADOG, Config.PropagationStyle.B3] From a4d53b0dcd128fcb72f27923f570befde9069952 Mon Sep 17 00:00:00 2001 From: Tyler Benson Date: Thu, 13 Jun 2019 14:18:51 -0700 Subject: [PATCH 21/22] Update Cassandra Tests and more instance name cleanup --- .../cassandra/CassandraClientDecorator.java | 13 +- .../test/groovy/CassandraClientTest.groovy | 133 +++++++++++------- .../instrumentation/jdbc/JDBCDecorator.java | 6 +- .../lettuce/LettuceClientDecorator.java | 5 +- .../test/groovy/LettuceAsyncClientTest.groovy | 2 - .../test/groovy/LettuceSyncClientTest.groovy | 2 - .../src/test/groovy/Netty40ClientTest.groovy | 3 +- .../src/test/groovy/Netty41ClientTest.groovy | 3 +- .../agent/test/base/HttpClientTest.groovy | 25 +--- .../trace/agent/test/utils/TraceUtils.groovy | 17 +++ 10 files changed, 114 insertions(+), 95 deletions(-) diff --git a/dd-java-agent/instrumentation/datastax-cassandra-3/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientDecorator.java b/dd-java-agent/instrumentation/datastax-cassandra-3/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientDecorator.java index 98eaab8e5e..e757311252 100644 --- a/dd-java-agent/instrumentation/datastax-cassandra-3/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientDecorator.java +++ b/dd-java-agent/instrumentation/datastax-cassandra-3/src/main/java/datadog/trace/instrumentation/datastax/cassandra/CassandraClientDecorator.java @@ -7,9 +7,6 @@ import datadog.trace.agent.decorator.DatabaseClientDecorator; import datadog.trace.api.DDSpanTypes; import io.opentracing.Span; import io.opentracing.tag.Tags; -import java.net.Inet4Address; -import java.net.InetAddress; -import java.nio.ByteBuffer; public class CassandraClientDecorator extends DatabaseClientDecorator { public static final CassandraClientDecorator DECORATE = new CassandraClientDecorator(); @@ -53,15 +50,7 @@ public class CassandraClientDecorator extends DatabaseClientDecorator { if (result != null) { final Host host = result.getExecutionInfo().getQueriedHost(); Tags.PEER_PORT.set(span, host.getSocketAddress().getPort()); - Tags.PEER_HOSTNAME.set(span, host.getAddress().getHostName()); - - final InetAddress inetAddress = host.getSocketAddress().getAddress(); - if (inetAddress instanceof Inet4Address) { - final byte[] address = inetAddress.getAddress(); - Tags.PEER_HOST_IPV4.set(span, ByteBuffer.wrap(address).getInt()); - } else { - Tags.PEER_HOST_IPV6.set(span, inetAddress.getHostAddress()); - } + onPeerConnection(span, host.getSocketAddress().getAddress()); } return span; } diff --git a/dd-java-agent/instrumentation/datastax-cassandra-3/src/test/groovy/CassandraClientTest.groovy b/dd-java-agent/instrumentation/datastax-cassandra-3/src/test/groovy/CassandraClientTest.groovy index 14c55c407c..18968c1ea1 100644 --- a/dd-java-agent/instrumentation/datastax-cassandra-3/src/test/groovy/CassandraClientTest.groovy +++ b/dd-java-agent/instrumentation/datastax-cassandra-3/src/test/groovy/CassandraClientTest.groovy @@ -2,15 +2,21 @@ import com.datastax.driver.core.Cluster import com.datastax.driver.core.Session import datadog.opentracing.DDSpan import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.test.asserts.TraceAssert import datadog.trace.api.DDSpanTypes import io.opentracing.tag.Tags import org.cassandraunit.utils.EmbeddedCassandraServerHelper import spock.lang.Shared +import static datadog.trace.agent.test.utils.TraceUtils.basicSpan +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace + class CassandraClientTest extends AgentTestRunner { @Shared Cluster cluster + @Shared + int port = 9142 def setupSpec() { /* @@ -34,72 +40,91 @@ class CassandraClientTest extends AgentTestRunner { EmbeddedCassandraServerHelper.cleanEmbeddedCassandra() } - def "sync traces"() { + def "test sync"() { setup: - final Session session = cluster.newSession() + Session session = cluster.connect(keyspace) - session.execute("DROP KEYSPACE IF EXISTS sync_test") - session.execute( - "CREATE KEYSPACE sync_test WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':3}") - session.execute("CREATE TABLE sync_test.users ( id UUID PRIMARY KEY, name text )") - session.execute("INSERT INTO sync_test.users (id, name) values (uuid(), 'alice')") - session.execute("SELECT * FROM sync_test.users where name = 'alice' ALLOW FILTERING") - - def query = "SELECT * FROM sync_test.users where name = 'alice' ALLOW FILTERING" + session.execute(statement) expect: - session.getClass().getName().endsWith("cassandra.TracingSession") - TEST_WRITER.size() == 5 - final DDSpan selectTrace = TEST_WRITER.get(TEST_WRITER.size() - 1).get(0) + assertTraces(keyspace ? 2 : 1) { + if (keyspace) { + trace(0, 1) { + cassandraSpan(it, 0, "USE $keyspace", null) + } + } + trace(keyspace ? 1 : 0, 1) { + cassandraSpan(it, 0, statement, keyspace) + } + } - selectTrace.getServiceName() == "cassandra" - selectTrace.getOperationName() == "cassandra.query" - selectTrace.getResourceName() == query - selectTrace.getSpanType() == DDSpanTypes.CASSANDRA + cleanup: + session.close() - selectTrace.getTags().get(Tags.COMPONENT.getKey()) == "java-cassandra" - selectTrace.getTags().get(Tags.DB_TYPE.getKey()) == "cassandra" - selectTrace.getTags().get(Tags.PEER_HOSTNAME.getKey()) == "localhost" - // More info about IPv4 tag: https://trello.com/c/2el2IwkF/174-mongodb-ot-contrib-provides-a-wrong-peeripv4 - selectTrace.getTags().get(Tags.PEER_HOST_IPV4.getKey()) == 2130706433 - selectTrace.getTags().get(Tags.PEER_PORT.getKey()) == 9142 - selectTrace.getTags().get(Tags.SPAN_KIND.getKey()) == "client" + where: + statement | keyspace + "DROP KEYSPACE IF EXISTS sync_test" | null + "CREATE KEYSPACE sync_test WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':3}" | null + "CREATE TABLE sync_test.users ( id UUID PRIMARY KEY, name text )" | "sync_test" + "INSERT INTO sync_test.users (id, name) values (uuid(), 'alice')" | "sync_test" + "SELECT * FROM users where name = 'alice' ALLOW FILTERING" | "sync_test" } - def "async traces"() { + def "test async"() { setup: - final Session session = cluster.connectAsync().get() - - session.executeAsync("DROP KEYSPACE IF EXISTS async_test").get() - session - .executeAsync( - "CREATE KEYSPACE async_test WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':3}") - .get() - session.executeAsync("CREATE TABLE async_test.users ( id UUID PRIMARY KEY, name text )").get() - session.executeAsync("INSERT INTO async_test.users (id, name) values (uuid(), 'alice')").get() - TEST_WRITER.waitForTraces(4) - session - .executeAsync("SELECT * FROM async_test.users where name = 'alice' ALLOW FILTERING") - .get() - TEST_WRITER.waitForTraces(5) - - def query = "SELECT * FROM async_test.users where name = 'alice' ALLOW FILTERING" + Session session = cluster.connect(keyspace) + runUnderTrace("parent") { + session.executeAsync(statement) + blockUntilChildSpansFinished(1) + } expect: - session.getClass().getName().endsWith("cassandra.TracingSession") - final DDSpan selectTrace = TEST_WRITER.get(TEST_WRITER.size() - 1).get(0) + assertTraces(keyspace ? 2 : 1) { + if (keyspace) { + trace(0, 1) { + cassandraSpan(it, 0, "USE $keyspace", null) + } + } + trace(keyspace ? 1 : 0, 2) { + basicSpan(it, 0, "parent") + cassandraSpan(it, 1, statement, keyspace, span(0)) + } + } - selectTrace.getServiceName() == "cassandra" - selectTrace.getOperationName() == "cassandra.query" - selectTrace.getResourceName() == query - selectTrace.getSpanType() == DDSpanTypes.CASSANDRA + cleanup: + session.close() - selectTrace.getTags().get(Tags.COMPONENT.getKey()) == "java-cassandra" - selectTrace.getTags().get(Tags.DB_TYPE.getKey()) == "cassandra" - selectTrace.getTags().get(Tags.PEER_HOSTNAME.getKey()) == "localhost" - // More info about IPv4 tag: https://trello.com/c/2el2IwkF/174-mongodb-ot-contrib-provides-a-wrong-peeripv4 - selectTrace.getTags().get(Tags.PEER_HOST_IPV4.getKey()) == 2130706433 - selectTrace.getTags().get(Tags.PEER_PORT.getKey()) == 9142 - selectTrace.getTags().get(Tags.SPAN_KIND.getKey()) == "client" + where: + statement | keyspace + "DROP KEYSPACE IF EXISTS async_test" | null + "CREATE KEYSPACE async_test WITH REPLICATION = {'class':'SimpleStrategy', 'replication_factor':3}" | null + "CREATE TABLE async_test.users ( id UUID PRIMARY KEY, name text )" | "async_test" + "INSERT INTO async_test.users (id, name) values (uuid(), 'alice')" | "async_test" + "SELECT * FROM users where name = 'alice' ALLOW FILTERING" | "async_test" } + + def cassandraSpan(TraceAssert trace, int index, String statement, String keyspace, Object parentSpan = null, Throwable exception = null) { + trace.span(index) { + serviceName "cassandra" + operationName "cassandra.query" + resourceName statement + spanType DDSpanTypes.CASSANDRA + if (parentSpan == null) { + parent() + } else { + childOf((DDSpan) parentSpan) + } + tags { + "$Tags.COMPONENT.key" "java-cassandra" + "$Tags.SPAN_KIND.key" Tags.SPAN_KIND_CLIENT + "$Tags.DB_INSTANCE.key" keyspace + "$Tags.DB_TYPE.key" "cassandra" + "$Tags.PEER_HOSTNAME.key" "localhost" + "$Tags.PEER_HOST_IPV4.key" "127.0.0.1" + "$Tags.PEER_PORT.key" port + defaultTags() + } + } + } + } diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java index 8540f2ab67..8c5f12a7b0 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/JDBCDecorator.java @@ -47,7 +47,11 @@ public class JDBCDecorator extends DatabaseClientDecorator { @Override protected String dbInstance(final DBInfo info) { - return info.getInstance(); + if (info.getInstance() != null) { + return info.getInstance(); + } else { + return info.getDb(); + } } public Span onConnection(final Span span, final Connection connection) { diff --git a/dd-java-agent/instrumentation/lettuce-5/src/main/java8/datadog/trace/instrumentation/lettuce/LettuceClientDecorator.java b/dd-java-agent/instrumentation/lettuce-5/src/main/java8/datadog/trace/instrumentation/lettuce/LettuceClientDecorator.java index 491bdb6448..f05674b500 100644 --- a/dd-java-agent/instrumentation/lettuce-5/src/main/java8/datadog/trace/instrumentation/lettuce/LettuceClientDecorator.java +++ b/dd-java-agent/instrumentation/lettuce-5/src/main/java8/datadog/trace/instrumentation/lettuce/LettuceClientDecorator.java @@ -43,7 +43,7 @@ public class LettuceClientDecorator extends DatabaseClientDecorator { @Override protected String dbInstance(final RedisURI connection) { - return connection.getHost() + ":" + connection.getPort() + "/" + connection.getDatabase(); + return null; } @Override @@ -53,7 +53,8 @@ public class LettuceClientDecorator extends DatabaseClientDecorator { Tags.PEER_PORT.set(span, connection.getPort()); span.setTag("db.redis.dbIndex", connection.getDatabase()); - span.setTag(DDTags.RESOURCE_NAME, "CONNECT:" + dbInstance(connection)); + span.setTag(DDTags.RESOURCE_NAME, "CONNECT:" + connection.getHost() + + ":" + connection.getPort() + "/" + connection.getDatabase()); } return super.onConnection(span, connection); } diff --git a/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceAsyncClientTest.groovy b/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceAsyncClientTest.groovy index 81ab5a9da5..5af20d06d0 100644 --- a/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceAsyncClientTest.groovy +++ b/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceAsyncClientTest.groovy @@ -123,7 +123,6 @@ class LettuceAsyncClientTest extends AgentTestRunner { tags { defaultTags() "component" "redis-client" - "db.instance" dbAddr "db.redis.dbIndex" 0 "db.type" "redis" "peer.hostname" HOST @@ -163,7 +162,6 @@ class LettuceAsyncClientTest extends AgentTestRunner { tags { defaultTags() "component" "redis-client" - "db.instance" dbAddrNonExistent "db.redis.dbIndex" 0 "db.type" "redis" errorTags CompletionException, String diff --git a/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceSyncClientTest.groovy b/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceSyncClientTest.groovy index 9bb25e2416..f328eb240b 100644 --- a/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceSyncClientTest.groovy +++ b/dd-java-agent/instrumentation/lettuce-5/src/test/groovy/LettuceSyncClientTest.groovy @@ -103,7 +103,6 @@ class LettuceSyncClientTest extends AgentTestRunner { tags { defaultTags() "component" "redis-client" - "db.instance" dbAddr "db.redis.dbIndex" 0 "db.type" "redis" "peer.hostname" HOST @@ -140,7 +139,6 @@ class LettuceSyncClientTest extends AgentTestRunner { tags { defaultTags() "component" "redis-client" - "db.instance" dbAddrNonExistent "db.redis.dbIndex" 0 "db.type" "redis" errorTags CompletionException, String diff --git a/dd-java-agent/instrumentation/netty-4.0/src/test/groovy/Netty40ClientTest.groovy b/dd-java-agent/instrumentation/netty-4.0/src/test/groovy/Netty40ClientTest.groovy index 0961415733..cdf7c74d77 100644 --- a/dd-java-agent/instrumentation/netty-4.0/src/test/groovy/Netty40ClientTest.groovy +++ b/dd-java-agent/instrumentation/netty-4.0/src/test/groovy/Netty40ClientTest.groovy @@ -9,6 +9,7 @@ import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT +import static datadog.trace.agent.test.utils.TraceUtils.basicSpan import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace import static org.asynchttpclient.Dsl.asyncHttpClient @@ -65,7 +66,7 @@ class Netty40ClientTest extends HttpClientTest { and: assertTraces(1) { trace(0, 2) { - parentSpan(it, 0, thrownException) + basicSpan(it, 0, "parent", thrownException) span(1) { operationName "netty.connect" diff --git a/dd-java-agent/instrumentation/netty-4.1/src/test/groovy/Netty41ClientTest.groovy b/dd-java-agent/instrumentation/netty-4.1/src/test/groovy/Netty41ClientTest.groovy index e8f722e899..0a76910b66 100644 --- a/dd-java-agent/instrumentation/netty-4.1/src/test/groovy/Netty41ClientTest.groovy +++ b/dd-java-agent/instrumentation/netty-4.1/src/test/groovy/Netty41ClientTest.groovy @@ -10,6 +10,7 @@ import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT +import static datadog.trace.agent.test.utils.TraceUtils.basicSpan import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace import static org.asynchttpclient.Dsl.asyncHttpClient @@ -67,7 +68,7 @@ class Netty41ClientTest extends HttpClientTest { and: assertTraces(1) { trace(0, 2) { - parentSpan(it, 0, thrownException) + basicSpan(it, 0, "parent", thrownException) span(1) { operationName "netty.connect" diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy index 5bc4818091..cc949da32c 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy @@ -16,6 +16,7 @@ import java.util.concurrent.ExecutionException import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer import static datadog.trace.agent.test.utils.ConfigUtils.withConfigOverride import static datadog.trace.agent.test.utils.PortUtils.UNUSABLE_PORT +import static datadog.trace.agent.test.utils.TraceUtils.basicSpan import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace import static org.junit.Assume.assumeTrue @@ -108,7 +109,7 @@ abstract class HttpClientTest extends AgentTestRu assertTraces(2) { server.distributedRequestTrace(it, 0, trace(1).last()) trace(1, size(2)) { - parentSpan(it, 0) + basicSpan(it, 0, "parent") clientSpan(it, 1, span(0), method, false) } } @@ -150,7 +151,7 @@ abstract class HttpClientTest extends AgentTestRu // only one trace (client). assertTraces(1) { trace(0, size(2)) { - parentSpan(it, 0) + basicSpan(it, 0, "parent") clientSpan(it, 1, span(0), method, renameService) } } @@ -173,7 +174,7 @@ abstract class HttpClientTest extends AgentTestRu // only one trace (client). assertTraces(1) { trace(0, size(3)) { - parentSpan(it, 0) + basicSpan(it, 0, "parent") span(1) { operationName "child" childOf span(0) @@ -304,7 +305,7 @@ abstract class HttpClientTest extends AgentTestRu and: assertTraces(1) { trace(0, 2) { - parentSpan(it, 0, thrownException) + basicSpan(it, 0, "parent", thrownException) clientSpan(it, 1, span(0), method, false, false, uri, null, thrownException) } } @@ -313,22 +314,6 @@ abstract class HttpClientTest extends AgentTestRu method = "GET" } - void parentSpan(TraceAssert trace, int index, Throwable exception = null) { - trace.span(index) { - parent() - serviceName "unnamed-java-app" - operationName "parent" - resourceName "parent" - errored exception != null - tags { - defaultTags() - if (exception) { - errorTags(exception.class, exception.message) - } - } - } - } - // parent span must be cast otherwise it breaks debugging classloading (junit loads it early) void clientSpan(TraceAssert trace, int index, Object parentSpan, String method = "GET", boolean renameService = false, boolean tagQueryString = false, URI uri = server.address.resolve("/success"), Integer status = 200, Throwable exception = null) { trace.span(index) { diff --git a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy index db2dc132fc..c349504cb1 100644 --- a/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy +++ b/dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/utils/TraceUtils.groovy @@ -1,6 +1,7 @@ package datadog.trace.agent.test.utils import datadog.trace.agent.decorator.BaseDecorator +import datadog.trace.agent.test.asserts.TraceAssert import datadog.trace.context.TraceScope import io.opentracing.Scope import io.opentracing.util.GlobalTracer @@ -40,4 +41,20 @@ class TraceUtils { scope.close() } } + + static basicSpan(TraceAssert trace, int index, String spanName, Throwable exception = null) { + trace.span(index) { + parent() + serviceName "unnamed-java-app" + operationName spanName + resourceName spanName + errored exception != null + tags { + defaultTags() + if (exception) { + errorTags(exception.class, exception.message) + } + } + } + } } From f37e4a2ec266301a43784960b6518012e98d35eb Mon Sep 17 00:00:00 2001 From: Nikolay Martynov Date: Fri, 14 Jun 2019 11:18:17 -0400 Subject: [PATCH 22/22] Revert "Collect more debugging info" This reverts commit a0fc54936a2dc6fe64e6d5cd1f23398279bc381c. --- .circleci/collect_reports.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/.circleci/collect_reports.sh b/.circleci/collect_reports.sh index 3e7891bd24..9cb27f8e94 100755 --- a/.circleci/collect_reports.sh +++ b/.circleci/collect_reports.sh @@ -19,9 +19,6 @@ function save_reports () { report_path=$REPORTS_DIR/$project_to_save mkdir -p $report_path cp -r workspace/$project_to_save/build/reports/* $report_path/ - cp -r workspace/$project_to_save/build/core* $report_path/ || true - cp -r workspace/$project_to_save/build/hs_err_pid* $report_path/ || true - cp -r workspace/$project_to_save/build/replay* $report_path/ || true } shopt -s globstar