diff --git a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java index 733d40c8793..681b0800590 100644 --- a/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java +++ b/dd-java-agent/instrumentation/jdbc/src/main/java/datadog/trace/instrumentation/jdbc/AbstractPreparedStatementInstrumentation.java @@ -5,6 +5,7 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DBM_TRACE_INJECTED; import static datadog.trace.instrumentation.jdbc.JDBCDecorator.DATABASE_QUERY; +import static datadog.trace.instrumentation.jdbc.JDBCDecorator.DBM_TRACE_PREPARED_STATEMENTS; import static datadog.trace.instrumentation.jdbc.JDBCDecorator.DECORATE; import static datadog.trace.instrumentation.jdbc.JDBCDecorator.INJECT_COMMENT; import static datadog.trace.instrumentation.jdbc.JDBCDecorator.logMissingQueryInfo; @@ -80,12 +81,19 @@ public static AgentScope onEnter(@Advice.This final Statement statement) { connection, InstrumentationContext.get(Connection.class, DBInfo.class)); final boolean injectTraceContext = DECORATE.shouldInjectTraceContext(dbInfo); - if (INJECT_COMMENT && injectTraceContext && DECORATE.isSqlServer(dbInfo)) { - // The span ID is pre-determined so that we can reference it when setting the context - final long spanID = DECORATE.setContextInfo(connection, dbInfo); - // we then force that pre-determined span ID for the span covering the actual query - span = AgentTracer.get().buildSpan(DATABASE_QUERY).withSpanId(spanID).start(); - span.setTag(DBM_TRACE_INJECTED, true); + if (INJECT_COMMENT && injectTraceContext) { + if (DECORATE.isSqlServer(dbInfo)) { + // The span ID is pre-determined so that we can reference it when setting the context + final long spanID = DECORATE.setContextInfo(connection, dbInfo); + // we then force that pre-determined span ID for the span covering the actual query + span = AgentTracer.get().buildSpan(DATABASE_QUERY).withSpanId(spanID).start(); + span.setTag(DBM_TRACE_INJECTED, true); + } else if (DECORATE.isPostgres(dbInfo) && DBM_TRACE_PREPARED_STATEMENTS) { + span = startSpan(DATABASE_QUERY); + DECORATE.setApplicationName(span, connection); + } else { + span = startSpan(DATABASE_QUERY); + } } else { span = startSpan(DATABASE_QUERY); } 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 e44334452ee..ea2b18e48f3 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 @@ -1,6 +1,8 @@ package datadog.trace.instrumentation.jdbc; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activateSpan; +import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.DBM_TRACE_INJECTED; +import static datadog.trace.bootstrap.instrumentation.api.InstrumentationTags.INSTRUMENTATION_TIME_MS; import static datadog.trace.bootstrap.instrumentation.api.Tags.*; import datadog.trace.api.Config; @@ -53,6 +55,8 @@ public class JDBCDecorator extends DatabaseClientDecorator { || DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_STATIC); private static final boolean INJECT_TRACE_CONTEXT = DBM_PROPAGATION_MODE.equals(DBM_PROPAGATION_MODE_FULL); + public static final boolean DBM_TRACE_PREPARED_STATEMENTS = + Config.get().isDBMTracePreparedStatements(); private volatile boolean warnedAboutDBMPropagationMode = false; // to log a warning only once @@ -248,6 +252,10 @@ public String traceParent(AgentSpan span, int samplingPriority) { return sb.toString(); } + public boolean isPostgres(final DBInfo dbInfo) { + return dbInfo.getType().startsWith("postgres"); + } + public boolean isSqlServer(final DBInfo dbInfo) { return "sqlserver".equals(dbInfo.getType()); } @@ -312,6 +320,44 @@ public long setContextInfo(Connection connection, DBInfo dbInfo) { return spanID; } + /** + * Executes `SET application_name` statement on the Postgres DB to set the trace parent in + * `pg_stat_activity.application_name`. This is used for prepared statements where it isn't + * possible to propagate trace parent with the comment. Downside: makes an additional round trip + * to the database. + * + * @param span The span of the instrumented statement + * @param connection The same connection as the one that will be used for the actual statement + */ + public void setApplicationName(AgentSpan span, Connection connection) { + final long startTime = System.currentTimeMillis(); + try { + + Integer priority = span.forceSamplingDecision(); + if (priority == null) { + return; + } + final String traceParent = DECORATE.traceParent(span, priority); + final String traceContext = "_DD_" + traceParent; + + connection.setClientInfo("ApplicationName", traceContext); + } catch (Throwable e) { + if (log.isDebugEnabled()) { + log.debug( + "Failed to set extra DBM data in application_name for trace {}. " + + "To disable this behavior, set trace_prepared_statements to 'false'. " + + "See https://docs.datadoghq.com/database_monitoring/connect_dbm_and_apm/ for more info.{}", + span.getTraceId().toHexString(), + e); + } + DECORATE.onError(span, e); + } finally { + span.setTag(DBM_TRACE_INJECTED, true); + final long elapsed = System.currentTimeMillis() - startTime; + span.setTag(INSTRUMENTATION_TIME_MS, elapsed); + } + } + @Override protected void postProcessServiceAndOperationName( AgentSpan span, DatabaseClientDecorator.NamingEntry namingEntry) { diff --git a/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy b/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy index a896ab2f33f..3f6c4ac2ecc 100644 --- a/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy +++ b/dd-java-agent/instrumentation/jdbc/src/test/groovy/RemoteJDBCInstrumentationTest.groovy @@ -28,6 +28,7 @@ import java.util.concurrent.TimeUnit import static datadog.trace.agent.test.utils.TraceUtils.basicSpan import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE +import static datadog.trace.api.config.TraceInstrumentationConfig.DB_DBM_TRACE_PREPARED_STATEMENTS // workaround for SSLHandShakeException on J9 only with Hikari/MySQL @Requires({ !System.getProperty("java.vendor").contains("IBM") }) @@ -347,9 +348,9 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { resultSet.next() resultSet.getInt(1) == 3 def addDbmTag = dbmTraceInjected() - if (driver == POSTGRESQL || driver == MYSQL || !addDbmTag) { + if (driver == SQLSERVER && addDbmTag){ assertTraces(1) { - trace(2) { + trace(3) { basicSpan(it, "parent") span { operationName this.operation(this.getDbType(driver)) @@ -364,72 +365,76 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() - // only set when there is an out of proc instance (postgresql, mysql) "$Tags.PEER_HOSTNAME" String - // currently there is a bug in the instrumentation with - // postgresql and mysql if the connection event is missed - // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } "$Tags.DB_OPERATION" operation if (usingHikari) { "$Tags.DB_POOL_NAME" String } + if (addDbmTag) { + "$InstrumentationTags.DBM_TRACE_INJECTED" true + } peerServiceFrom(Tags.DB_INSTANCE) defaultTags() } } - } - } - } else { - assertTraces(1) { - trace(3) { - basicSpan(it, "parent") span { - operationName this.operation(this.getDbType(driver)) serviceName service(driver) - resourceName obfuscatedQuery + operationName this.operation(this.getDbType(driver)) + resourceName "set context_info ?" spanType DDSpanTypes.SQL childOf span(0) errored false measured true tags { - "$Tags.COMPONENT" "java-jdbc-prepared_statement" + "$Tags.COMPONENT" "java-jdbc-statement" "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() "$Tags.PEER_HOSTNAME" String "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } - "$Tags.DB_OPERATION" operation + "$Tags.DB_OPERATION" "set" if (usingHikari) { "$Tags.DB_POOL_NAME" String } - if (addDbmTag) { - "$InstrumentationTags.DBM_TRACE_INJECTED" true - } + "dd.instrumentation" true peerServiceFrom(Tags.DB_INSTANCE) defaultTags() } } + } + } + } else { + assertTraces(1) { + trace(2) { + basicSpan(it, "parent") span { - serviceName service(driver) operationName this.operation(this.getDbType(driver)) - resourceName "set context_info ?" + serviceName service(driver) + resourceName obfuscatedQuery spanType DDSpanTypes.SQL childOf span(0) errored false measured true tags { - "$Tags.COMPONENT" "java-jdbc-statement" + "$Tags.COMPONENT" "java-jdbc-prepared_statement" "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() + // only set when there is an out of proc instance (postgresql, mysql) "$Tags.PEER_HOSTNAME" String + // currently there is a bug in the instrumentation with + // postgresql and mysql if the connection event is missed + // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } - "$Tags.DB_OPERATION" "set" + "$Tags.DB_OPERATION" operation if (usingHikari) { "$Tags.DB_POOL_NAME" String } - "dd.instrumentation" true + if (this.dbmTracePreparedStatements(driver)){ + "$InstrumentationTags.DBM_TRACE_INJECTED" true + "$InstrumentationTags.INSTRUMENTATION_TIME_MS" Long + } peerServiceFrom(Tags.DB_INSTANCE) defaultTags() } @@ -470,9 +475,11 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { then: resultSet.next() resultSet.getInt(1) == 3 - if (driver == POSTGRESQL || driver == MYSQL || !dbmTraceInjected()) { + + def addDbmTag = dbmTraceInjected() + if (driver == SQLSERVER && addDbmTag){ assertTraces(1) { - trace(2) { + trace(3) { basicSpan(it, "parent") span { operationName this.operation(this.getDbType(driver)) @@ -497,64 +504,68 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { if (conPoolType == "hikari") { "$Tags.DB_POOL_NAME" String } + "$InstrumentationTags.DBM_TRACE_INJECTED" true peerServiceFrom(Tags.DB_INSTANCE) defaultTags() } } - } - } - } else { - assertTraces(1) { - trace(3) { - basicSpan(it, "parent") span { - operationName this.operation(this.getDbType(driver)) serviceName service(driver) - resourceName obfuscatedQuery + operationName this.operation(this.getDbType(driver)) + resourceName "set context_info ?" spanType DDSpanTypes.SQL childOf span(0) errored false measured true tags { - "$Tags.COMPONENT" "java-jdbc-prepared_statement" + "$Tags.COMPONENT" "java-jdbc-statement" "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() - // only set when there is an out of proc instance (postgresql, mysql) "$Tags.PEER_HOSTNAME" String - // currently there is a bug in the instrumentation with - // postgresql and mysql if the connection event is missed - // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } - "$Tags.DB_OPERATION" operation + "$Tags.DB_OPERATION" "set" if (conPoolType == "hikari") { "$Tags.DB_POOL_NAME" String } - "$InstrumentationTags.DBM_TRACE_INJECTED" true + "dd.instrumentation" true peerServiceFrom(Tags.DB_INSTANCE) defaultTags() } } + } + } + } else { + assertTraces(1) { + trace(2) { + basicSpan(it, "parent") span { - serviceName service(driver) operationName this.operation(this.getDbType(driver)) - resourceName "set context_info ?" + serviceName service(driver) + resourceName obfuscatedQuery spanType DDSpanTypes.SQL childOf span(0) errored false measured true tags { - "$Tags.COMPONENT" "java-jdbc-statement" + "$Tags.COMPONENT" "java-jdbc-prepared_statement" "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() + // only set when there is an out of proc instance (postgresql, mysql) "$Tags.PEER_HOSTNAME" String + // currently there is a bug in the instrumentation with + // postgresql and mysql if the connection event is missed + // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } - "$Tags.DB_OPERATION" "set" + "$Tags.DB_OPERATION" operation if (conPoolType == "hikari") { "$Tags.DB_POOL_NAME" String } - "dd.instrumentation" true + if (this.dbmTracePreparedStatements(driver)){ + "$InstrumentationTags.DBM_TRACE_INJECTED" true + "$InstrumentationTags.INSTRUMENTATION_TIME_MS" Long + } peerServiceFrom(Tags.DB_INSTANCE) defaultTags() } @@ -595,9 +606,10 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { then: resultSet.next() resultSet.getInt(1) == 3 - if (driver == POSTGRESQL || driver == MYSQL || !dbmTraceInjected()) { + def addDbmTag = dbmTraceInjected() + if (driver == SQLSERVER && addDbmTag){ assertTraces(1) { - trace(2) { + trace(3) { basicSpan(it, "parent") span { operationName this.operation(this.getDbType(driver)) @@ -612,69 +624,73 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() - // only set when there is an out of proc instance (postgresql, mysql) "$Tags.PEER_HOSTNAME" String - // currently there is a bug in the instrumentation with - // postgresql and mysql if the connection event is missed - // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } "${Tags.DB_OPERATION}" operation if (conPoolType == "hikari") { "$Tags.DB_POOL_NAME" String } + "$InstrumentationTags.DBM_TRACE_INJECTED" true defaultTags() } } - } - } - } else { - assertTraces(1) { - trace(3) { - basicSpan(it, "parent") span { - operationName this.operation(this.getDbType(driver)) serviceName service(driver) - resourceName obfuscatedQuery + operationName this.operation(this.getDbType(driver)) + resourceName "set context_info ?" spanType DDSpanTypes.SQL childOf span(0) errored false measured true tags { - "$Tags.COMPONENT" "java-jdbc-prepared_statement" + "$Tags.COMPONENT" "java-jdbc-statement" "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() "$Tags.PEER_HOSTNAME" String "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } - "${Tags.DB_OPERATION}" operation + "$Tags.DB_OPERATION" "set" + "dd.instrumentation" true if (conPoolType == "hikari") { "$Tags.DB_POOL_NAME" String } - "$InstrumentationTags.DBM_TRACE_INJECTED" true + peerServiceFrom(Tags.DB_INSTANCE) defaultTags() } } + } + } + } else { + assertTraces(1) { + trace(2) { + basicSpan(it, "parent") span { - serviceName service(driver) operationName this.operation(this.getDbType(driver)) - resourceName "set context_info ?" + serviceName service(driver) + resourceName obfuscatedQuery spanType DDSpanTypes.SQL childOf span(0) errored false measured true tags { - "$Tags.COMPONENT" "java-jdbc-statement" + "$Tags.COMPONENT" "java-jdbc-prepared_statement" "$Tags.SPAN_KIND" Tags.SPAN_KIND_CLIENT "$Tags.DB_TYPE" this.getDbType(driver) "$Tags.DB_INSTANCE" dbName.get(driver).toLowerCase() + // only set when there is an out of proc instance (postgresql, mysql) "$Tags.PEER_HOSTNAME" String + // currently there is a bug in the instrumentation with + // postgresql and mysql if the connection event is missed + // since Connection.getClientInfo will not provide the username "$Tags.DB_USER" { it == null || it == jdbcUserNames.get(driver) } - "$Tags.DB_OPERATION" "set" - "dd.instrumentation" true + "${Tags.DB_OPERATION}" operation if (conPoolType == "hikari") { "$Tags.DB_POOL_NAME" String } - peerServiceFrom(Tags.DB_INSTANCE) + if (this.dbmTracePreparedStatements(driver)){ + "$InstrumentationTags.DBM_TRACE_INJECTED" true + "$InstrumentationTags.INSTRUMENTATION_TIME_MS" Long + } defaultTags() } } @@ -979,6 +995,10 @@ abstract class RemoteJDBCInstrumentationTest extends VersionedNamingTestBase { protected abstract String operation(String dbType) protected abstract boolean dbmTraceInjected() + + protected boolean dbmTracePreparedStatements(String dbType){ + return false + } } class RemoteJDBCInstrumentationV0Test extends RemoteJDBCInstrumentationTest { @@ -1067,3 +1087,44 @@ class RemoteDBMTraceInjectedForkedTest extends RemoteJDBCInstrumentationTest { return databaseNaming.normalizedName(dbType) } } + +class RemoteDBMTraceInjectedForkedTestTracePreparedStatements extends RemoteJDBCInstrumentationTest { + + @Override + void configurePreAgent() { + super.configurePreAgent() + injectSysConfig("dd.dbm.propagation.mode", "full") + injectSysConfig(DB_DBM_TRACE_PREPARED_STATEMENTS, "true") + } + + @Override + protected boolean dbmTraceInjected() { + return true + } + + @Override + int version() { + return 1 + } + + @Override + protected String service(String dbType) { + return Config.get().getServiceName() + } + + @Override + protected String operation(String dbType) { + return "${dbType}.query" + } + + @Override + protected String getDbType(String dbType) { + final databaseNaming = new DatabaseNamingV1() + return databaseNaming.normalizedName(dbType) + } + + @Override + protected boolean dbmTracePreparedStatements(String dbType){ + return dbType == POSTGRESQL + } +} diff --git a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java index 9c12e7dda8c..8b1dc24764b 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/ConfigDefaults.java @@ -69,6 +69,7 @@ public final class ConfigDefaults { static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX = false; static final boolean DEFAULT_DB_CLIENT_HOST_SPLIT_BY_HOST = false; static final String DEFAULT_DB_DBM_PROPAGATION_MODE_MODE = "disabled"; + static final boolean DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS = false; // Default value is set to 0, it disables the latency trace interceptor static final int DEFAULT_TRACE_KEEP_LATENCY_THRESHOLD_MS = 0; static final int DEFAULT_SCOPE_DEPTH_LIMIT = 100; diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java index 7fca0156733..13b9e175af7 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/TraceInstrumentationConfig.java @@ -68,6 +68,8 @@ public final class TraceInstrumentationConfig { public static final String DB_DBM_PROPAGATION_MODE_MODE = "dbm.propagation.mode"; + public static final String DB_DBM_TRACE_PREPARED_STATEMENTS = "dbm.trace_prepared_statements"; + public static final String JDBC_CONNECTION_CLASS_NAME = "trace.jdbc.connection.class.name"; public static final String HTTP_URL_CONNECTION_CLASS_NAME = diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index 8b5a2cc40d8..d61ccdc040b 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -375,6 +375,7 @@ public static String getHostName() { private final int remoteConfigMaxExtraServices; private final String DBMPropagationMode; + private final boolean DBMTracePreparedStatements; private final boolean debuggerEnabled; private final int debuggerUploadTimeout; @@ -846,6 +847,10 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins configProvider.getString( DB_DBM_PROPAGATION_MODE_MODE, DEFAULT_DB_DBM_PROPAGATION_MODE_MODE); + DBMTracePreparedStatements = + configProvider.getBoolean( + DB_DBM_TRACE_PREPARED_STATEMENTS, DEFAULT_DB_DBM_TRACE_PREPARED_STATEMENTS); + splitByTags = tryMakeImmutableSet(configProvider.getList(SPLIT_BY_TAGS)); springDataRepositoryInterfaceResourceName = @@ -3686,6 +3691,10 @@ public boolean isEnabled( Collections.singletonList(settingName), "", settingSuffix, defaultEnabled); } + public boolean isDBMTracePreparedStatements() { + return DBMTracePreparedStatements; + } + public String getDBMPropagationMode() { return DBMPropagationMode; } diff --git a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java index f21559fcbf4..ff94ab022d7 100644 --- a/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java +++ b/internal-api/src/main/java/datadog/trace/bootstrap/instrumentation/api/InstrumentationTags.java @@ -107,6 +107,7 @@ public class InstrumentationTags { public static final String TWILIO_STATUS = "twilio.status"; public static final String TWILIO_PARENT_SID = "twilio.parentSid"; public static final String DBM_TRACE_INJECTED = "_dd.dbm_trace_injected"; + public static final String INSTRUMENTATION_TIME_MS = "_dd.instrumentation.time_ms"; public static final UTF8BytesString DD_MEASURED = UTF8BytesString.create("_dd.measured"); public static final UTF8BytesString DD_TOP_LEVEL = UTF8BytesString.create("_dd.top_level"); public static final UTF8BytesString DD_PARTIAL_VERSION =