diff --git a/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/main/java/datadog/trace/instrumentation/couchbase_31/client/DatadogRequestTracer.java b/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/main/java/datadog/trace/instrumentation/couchbase_31/client/DatadogRequestTracer.java index f37dc7de3d6..3ef0efdf3b3 100644 --- a/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/main/java/datadog/trace/instrumentation/couchbase_31/client/DatadogRequestTracer.java +++ b/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/main/java/datadog/trace/instrumentation/couchbase_31/client/DatadogRequestTracer.java @@ -1,10 +1,12 @@ package datadog.trace.instrumentation.couchbase_31.client; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.blackholeSpan; import static datadog.trace.instrumentation.couchbase_31.client.CouchbaseClientDecorator.COUCHBASE_CLIENT; import com.couchbase.client.core.Core; import com.couchbase.client.core.cnc.RequestSpan; import com.couchbase.client.core.cnc.RequestTracer; +import datadog.trace.api.Config; import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; @@ -37,7 +39,12 @@ public RequestSpan requestSpan(String requestName, RequestSpan requestParent) { if (null == parent) { parent = tracer.activeSpan(); } + if (null != parent && COUCHBASE_CLIENT.equals(parent.getTag(Tags.COMPONENT))) { + if (!Config.get().isCouchbaseInternalSpansEnabled()) { + // mute the tracing related to internal spans + return DatadogRequestSpan.wrap(blackholeSpan(), coreContext); + } spanName = COUCHBASE_INTERNAL; measured = false; seedNodes = parent.getTag(InstrumentationTags.COUCHBASE_SEED_NODES); diff --git a/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/test/groovy/CouchbaseClient31Test.groovy b/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/test/groovy/CouchbaseClient31Test.groovy index 9dfe354032d..1c76f2a6cba 100644 --- a/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/test/groovy/CouchbaseClient31Test.groovy +++ b/dd-java-agent/instrumentation/couchbase/couchbase-3.1/src/test/groovy/CouchbaseClient31Test.groovy @@ -1,5 +1,4 @@ import com.couchbase.client.core.env.TimeoutConfig -import com.couchbase.client.core.error.CouchbaseException import com.couchbase.client.core.error.DocumentNotFoundException import com.couchbase.client.core.error.ParsingFailureException import com.couchbase.client.java.Bucket @@ -107,8 +106,9 @@ abstract class CouchbaseClient31Test extends VersionedNamingTestBase { } } - def "check basic error spans"() { + def "check basic error spans with internal spans enabled #internalEnabled"() { setup: + injectSysConfig("trace.couchbase.internal-spans.enabled", "$internalEnabled") def collection = bucket.defaultCollection() Throwable ex = null @@ -122,7 +122,7 @@ abstract class CouchbaseClient31Test extends VersionedNamingTestBase { then: assertTraces(1) { sortSpansByStart() - trace(2) { + trace(internalEnabled ? 2 : 1) { assertCouchbaseCall(it, "cb.get", [ 'db.couchbase.collection': '_default', 'db.couchbase.retries' : { Long }, @@ -131,9 +131,15 @@ abstract class CouchbaseClient31Test extends VersionedNamingTestBase { 'db.name' : BUCKET, 'db.operation' : 'get', ], false, ex) - assertCouchbaseDispatchCall(it, span(0)) + if (internalEnabled) { + assertCouchbaseDispatchCall(it, span(0)) + } } } + where: + internalEnabled | _ + true | _ + false | _ } def "check query spans"() { @@ -218,9 +224,11 @@ abstract class CouchbaseClient31Test extends VersionedNamingTestBase { adhoc << [true, false] } - def "check multiple query spans with parent and adhoc false"() { - def query = 'select count(1) from `test-bucket` where (`something` = "wonderful") limit 1' - def normalizedQuery = 'select count(?) from `test-bucket` where (`something` = "wonderful") limit ?' + def "check multiple query spans with parent and adhoc false and internal spans enabled = #internalEnabled"() { + setup: + injectSysConfig("trace.couchbase.internal-spans.enabled", "$internalEnabled") + def query = "select count(1) from `test-bucket` where (`something` = \"$queryArg\") limit 1" + def normalizedQuery = "select count(?) from `test-bucket` where (`something` = \"$queryArg\") limit ?" int count1 = 0 int count2 = 0 @@ -240,32 +248,40 @@ abstract class CouchbaseClient31Test extends VersionedNamingTestBase { } then: - count1 == 250 - count2 == 250 + count1 == expectedCount + count2 == expectedCount assertTraces(1) { sortSpansByStart() - trace(7) { + trace(internalEnabled ? 7 : 3) { basicSpan(it, 'multiple.parent') assertCouchbaseCall(it, "cb.query", [ 'db.couchbase.retries' : { Long }, 'db.couchbase.service' : 'query', ], normalizedQuery, span(0), false) - assertCouchbaseCall(it, "prepare", [ - 'db.couchbase.retries' : { Long }, - 'db.couchbase.service' : 'query', - ], "PREPARE $normalizedQuery", span(1), true) - assertCouchbaseDispatchCall(it, span(2)) + if (internalEnabled) { + assertCouchbaseCall(it, "prepare", [ + 'db.couchbase.retries': { Long }, + 'db.couchbase.service': 'query', + ], "PREPARE $normalizedQuery", span(1), true) + assertCouchbaseDispatchCall(it, span(2)) + } assertCouchbaseCall(it, "cb.query", [ 'db.couchbase.retries' : { Long }, 'db.couchbase.service' : 'query', ], normalizedQuery, span(0), false) - assertCouchbaseCall(it, "execute", [ - 'db.couchbase.retries' : { Long }, - 'db.couchbase.service' : 'query', - ], normalizedQuery, span(4), true) - assertCouchbaseDispatchCall(it, span(5)) + if (internalEnabled) { + assertCouchbaseCall(it, "execute", [ + 'db.couchbase.retries': { Long }, + 'db.couchbase.service': 'query', + ], normalizedQuery, span(4), true) + assertCouchbaseDispatchCall(it, span(5)) + } } } + where: + internalEnabled | queryArg | expectedCount + true | "wonderful" | 250 + false | "notinternal" | 0 // avoid having the query engine reusing previous prepared query } def "check error query spans with parent"() { @@ -299,68 +315,6 @@ abstract class CouchbaseClient31Test extends VersionedNamingTestBase { } } - def "check multiple error query spans with parent and adhoc false"() { - def query = 'select count(1) from `test-bucket` where (`something` = "wonderful") limeit 1' - def normalizedQuery = 'select count(?) from `test-bucket` where (`something` = "wonderful") limeit ?' - int count1 = 0 - int count2 = 0 - Throwable ex1 = null - Throwable ex2 = null - - when: - runUnderTrace('multiple.parent') { - // This results in a call to AsyncCluster.query(...) - try { - cluster.query(query, QueryOptions.queryOptions().adhoc(false)).each { - it.rowsAsObject().each { - count1 = it.getInt('$1') - } - } - } catch (CouchbaseException expected) { - ex1 = expected - } - try { - cluster.query(query, QueryOptions.queryOptions().adhoc(false)).each { - it.rowsAsObject().each { - count2 = it.getInt('$1') - } - } - } catch (CouchbaseException expected) { - ex2 = expected - } - } - - then: - count1 == 0 - count2 == 0 - ex1 != null - ex2 != null - assertTraces(1) { - sortSpansByStart() - trace(7) { - basicSpan(it, 'multiple.parent') - assertCouchbaseCall(it, "cb.query", [ - 'db.couchbase.retries' : { Long }, - 'db.couchbase.service' : 'query', - ], normalizedQuery, span(0), false, ex1) - assertCouchbaseCall(it, "prepare", [ - 'db.couchbase.retries' : { Long }, - 'db.couchbase.service' : 'query', - ], "PREPARE $normalizedQuery", span(1), true, ex1) - assertCouchbaseDispatchCall(it, span(2)) - assertCouchbaseCall(it, "cb.query", [ - 'db.couchbase.retries' : { Long }, - 'db.couchbase.service' : 'query', - ], normalizedQuery, span(0), false, ex2) - assertCouchbaseCall(it, "prepare", [ - 'db.couchbase.retries' : { Long }, - 'db.couchbase.service' : 'query', - ], "PREPARE $normalizedQuery", span(4), true, ex2) - assertCouchbaseDispatchCall(it, span(5)) - } - } - } - void assertCouchbaseCall(TraceAssert trace, String name, Map extraTags, boolean internal = false, Throwable ex = null) { assertCouchbaseCall(trace, name, extraTags, null, null, internal, ex) } diff --git a/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/main/java/datadog/trace/instrumentation/couchbase_32/client/DatadogRequestTracer.java b/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/main/java/datadog/trace/instrumentation/couchbase_32/client/DatadogRequestTracer.java index f887c6c4c71..f3bc994fa3c 100644 --- a/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/main/java/datadog/trace/instrumentation/couchbase_32/client/DatadogRequestTracer.java +++ b/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/main/java/datadog/trace/instrumentation/couchbase_32/client/DatadogRequestTracer.java @@ -1,11 +1,13 @@ package datadog.trace.instrumentation.couchbase_32.client; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.blackholeSpan; import static datadog.trace.instrumentation.couchbase_32.client.CouchbaseClientDecorator.COUCHBASE_CLIENT; import static datadog.trace.instrumentation.couchbase_32.client.CouchbaseClientDecorator.OPERATION_NAME; import com.couchbase.client.core.Core; import com.couchbase.client.core.cnc.RequestSpan; import com.couchbase.client.core.cnc.RequestTracer; +import datadog.trace.api.Config; import datadog.trace.bootstrap.ContextStore; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; @@ -39,24 +41,32 @@ public RequestSpan requestSpan(String requestName, RequestSpan requestParent) { if (null == parent) { parent = tracer.activeSpan(); } - if (null != parent && COUCHBASE_CLIENT.equals(parent.getTag(Tags.COMPONENT))) { - spanName = COUCHBASE_INTERNAL; - measured = false; - seedNodes = parent.getTag(InstrumentationTags.COUCHBASE_SEED_NODES); - } + DatadogRequestSpan requestSpan = null; - AgentTracer.SpanBuilder builder = tracer.buildSpan(spanName); - if (null != parent) { - builder.asChildOf(parent.context()); + if (null != parent && COUCHBASE_CLIENT.equals(parent.getTag(Tags.COMPONENT))) { + if (!Config.get().isCouchbaseInternalSpansEnabled()) { + // mute the tracing related to internal spans + requestSpan = DatadogRequestSpan.wrap(blackholeSpan(), coreContext); + } else { + spanName = COUCHBASE_INTERNAL; + measured = false; + seedNodes = parent.getTag(InstrumentationTags.COUCHBASE_SEED_NODES); + } } - AgentSpan span = builder.start(); - CouchbaseClientDecorator.DECORATE.afterStart(span); - span.setResourceName(requestName); - span.setMeasured(measured); - if (seedNodes != null) { - span.setTag(InstrumentationTags.COUCHBASE_SEED_NODES, seedNodes); + if (requestSpan == null) { + AgentTracer.SpanBuilder builder = tracer.buildSpan(spanName); + if (null != parent) { + builder.asChildOf(parent.context()); + } + AgentSpan span = builder.start(); + CouchbaseClientDecorator.DECORATE.afterStart(span); + span.setResourceName(requestName); + span.setMeasured(measured); + if (seedNodes != null) { + span.setTag(InstrumentationTags.COUCHBASE_SEED_NODES, seedNodes); + } + requestSpan = DatadogRequestSpan.wrap(span, coreContext); } - DatadogRequestSpan requestSpan = DatadogRequestSpan.wrap(span, coreContext); // When Couchbase converts a query to a prepare statement or execute statement, // it will not finish the original span switch (requestName) { diff --git a/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/test/groovy/CouchbaseClient32Test.groovy b/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/test/groovy/CouchbaseClient32Test.groovy index 0d851c6fcb8..2a8cd522888 100644 --- a/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/test/groovy/CouchbaseClient32Test.groovy +++ b/dd-java-agent/instrumentation/couchbase/couchbase-3.2/src/test/groovy/CouchbaseClient32Test.groovy @@ -112,8 +112,9 @@ abstract class CouchbaseClient32Test extends VersionedNamingTestBase { } } - def "check basic error spans"() { + def "check basic error spans with internal spans enabled #internalEnabled"() { setup: + injectSysConfig("trace.couchbase.internal-spans.enabled", "$internalEnabled") def collection = bucket.defaultCollection() Throwable ex = null @@ -128,7 +129,7 @@ abstract class CouchbaseClient32Test extends VersionedNamingTestBase { ex != null assertTraces(1) { sortSpansByStart() - trace(2) { + trace(internalEnabled ? 2: 1) { assertCouchbaseCall(it, "get", [ 'db.couchbase.collection' : '_default', 'db.couchbase.document_id': { String }, @@ -138,14 +139,18 @@ abstract class CouchbaseClient32Test extends VersionedNamingTestBase { 'db.name' : BUCKET, 'db.operation' : 'get' ], false, ex) - assertCouchbaseDispatchCall(it, span(0), [ - 'db.couchbase.collection' : '_default', - 'db.couchbase.document_id' : { String }, - 'db.couchbase.scope' : '_default', - 'db.name' : BUCKET - ]) + if (internalEnabled) { + assertCouchbaseDispatchCall(it, span(0), [ + 'db.couchbase.collection' : '_default', + 'db.couchbase.document_id' : { String }, + 'db.couchbase.scope' : '_default', + 'db.name' : BUCKET + ]) + } } } + where: + internalEnabled << [true, false] } def "check query spans"() { @@ -228,10 +233,11 @@ abstract class CouchbaseClient32Test extends VersionedNamingTestBase { adhoc << [true, false] } - def "check multiple async query spans with parent and adhoc false"() { + def "check multiple async query spans with parent and adhoc false and internal spans enabled = #internalEnabled"() { setup: - def query = 'select count(1) from `test-bucket` where (`something` = "wonderful") limit 1' - def normalizedQuery = 'select count(?) from `test-bucket` where (`something` = "wonderful") limit ?' + injectSysConfig("trace.couchbase.internal-spans.enabled", "$internalEnabled") + def query = "select count(1) from `test-bucket` where (`something` = \"$queryArg\") limit 1" + def normalizedQuery = "select count(?) from `test-bucket` where (`something` = \"$queryArg\") limit ?" int count1 = 0 int count2 = 0 def extraPrepare = isLatestDepTest @@ -252,38 +258,46 @@ abstract class CouchbaseClient32Test extends VersionedNamingTestBase { } then: - count1 == 250 - count2 == 250 + count1 == expectedCount + count2 == expectedCount assertTraces(1) { - sortSpansByStart() - trace(extraPrepare ? 8 : 7) { + trace(internalEnabled ? (extraPrepare ? 8 : 7) : 3) { + sortSpansByStart() basicSpan(it, 'async.multiple') assertCouchbaseCall(it, normalizedQuery, [ 'db.couchbase.retries' : { Long }, 'db.couchbase.service' : 'query' ], span(0)) - assertCouchbaseCall(it, "PREPARE $normalizedQuery", [ - 'db.couchbase.retries': { Long }, - 'db.couchbase.service': 'query' - ], span(1), true) - assertCouchbaseDispatchCall(it, span(2)) + if (internalEnabled) { + assertCouchbaseCall(it, "PREPARE $normalizedQuery", [ + 'db.couchbase.retries': { Long }, + 'db.couchbase.service': 'query' + ], span(1), true) + assertCouchbaseDispatchCall(it, span(2)) + } assertCouchbaseCall(it, normalizedQuery, [ 'db.couchbase.retries' : { Long }, 'db.couchbase.service' : 'query' ], span(0)) - if (extraPrepare) { - assertCouchbaseCall(it, "PREPARE $normalizedQuery", [ + if (internalEnabled) { + if (extraPrepare) { + assertCouchbaseCall(it, "PREPARE $normalizedQuery", [ + 'db.couchbase.retries': { Long }, + 'db.couchbase.service': 'query' + ], span(4), true) + } + assertCouchbaseCall(it, normalizedQuery, [ 'db.couchbase.retries': { Long }, 'db.couchbase.service': 'query' ], span(4), true) + assertCouchbaseDispatchCall(it, span(extraPrepare ? 6 : 5)) } - assertCouchbaseCall(it, normalizedQuery, [ - 'db.couchbase.retries': { Long }, - 'db.couchbase.service': 'query' - ], span(4), true) - assertCouchbaseDispatchCall(it, span(extraPrepare ? 6 : 5)) } } + where: + internalEnabled | queryArg | expectedCount + true | "wonderful" | 250 + false | "notinternal" | 0 // avoid having the query engine reusing previous prepared query } def "check error query spans with parent"() { 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 9b6c0958673..02f41c3a9ab 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 @@ -215,6 +215,7 @@ public final class ConfigDefaults { static final float DEFAULT_TRACE_FLUSH_INTERVAL = 1; + static final boolean DEFAULT_COUCHBASE_INTERNAL_SPANS_ENABLED = true; static final boolean DEFAULT_ELASTICSEARCH_BODY_ENABLED = false; static final boolean DEFAULT_ELASTICSEARCH_PARAMS_ENABLED = true; static final boolean DEFAULT_ELASTICSEARCH_BODY_AND_PARAMS_ENABLED = false; 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 29c9f3e37d6..ee64514f908 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 @@ -129,7 +129,8 @@ public final class TraceInstrumentationConfig { public static final String RESOLVER_USE_URL_CACHES = "resolver.use.url.caches"; public static final String RESOLVER_RESET_INTERVAL = "resolver.reset.interval"; public static final String RESOLVER_NAMES_ARE_UNIQUE = "resolver.names.are.unique"; - + public static final String COUCHBASE_INTERNAL_SPANS_ENABLED = + "trace.couchbase.internal-spans.enabled"; public static final String ELASTICSEARCH_BODY_ENABLED = "trace.elasticsearch.body.enabled"; public static final String ELASTICSEARCH_PARAMS_ENABLED = "trace.elasticsearch.params.enabled"; public static final String ELASTICSEARCH_BODY_AND_PARAMS_ENABLED = 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 5bfb75859fc..3a78f0bc40b 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -30,6 +30,7 @@ import static datadog.trace.api.ConfigDefaults.DEFAULT_CIVISIBILITY_SOURCE_DATA_ROOT_CHECK_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_CLIENT_IP_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_CLOCK_SYNC_PERIOD; +import static datadog.trace.api.ConfigDefaults.DEFAULT_COUCHBASE_INTERNAL_SPANS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_CWS_ENABLED; import static datadog.trace.api.ConfigDefaults.DEFAULT_CWS_TLS_REFRESH; import static datadog.trace.api.ConfigDefaults.DEFAULT_DATA_STREAMS_BUCKET_DURATION; @@ -338,6 +339,7 @@ import static datadog.trace.api.config.RemoteConfigConfig.REMOTE_CONFIG_TARGETS_KEY_ID; import static datadog.trace.api.config.RemoteConfigConfig.REMOTE_CONFIG_URL; import static datadog.trace.api.config.TraceInstrumentationConfig.AXIS_PROMOTE_RESOURCE_NAME; +import static datadog.trace.api.config.TraceInstrumentationConfig.COUCHBASE_INTERNAL_SPANS_ENABLED; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_HOST; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE; import static datadog.trace.api.config.TraceInstrumentationConfig.DB_CLIENT_HOST_SPLIT_BY_INSTANCE_TYPE_SUFFIX; @@ -912,6 +914,7 @@ static class HostNameHolder { private final boolean longRunningTraceEnabled; private final long longRunningTraceInitialFlushInterval; private final long longRunningTraceFlushInterval; + private final boolean couchbaseInternalSpansEnabled; private final boolean elasticsearchBodyEnabled; private final boolean elasticsearchParamsEnabled; private final boolean elasticsearchBodyAndParamsEnabled; @@ -999,6 +1002,9 @@ private Config(final ConfigProvider configProvider, final InstrumenterConfig ins } else { secureRandom = configProvider.getBoolean(SECURE_RANDOM, DEFAULT_SECURE_RANDOM); } + couchbaseInternalSpansEnabled = + configProvider.getBoolean( + COUCHBASE_INTERNAL_SPANS_ENABLED, DEFAULT_COUCHBASE_INTERNAL_SPANS_ENABLED); elasticsearchBodyEnabled = configProvider.getBoolean(ELASTICSEARCH_BODY_ENABLED, DEFAULT_ELASTICSEARCH_BODY_ENABLED); elasticsearchParamsEnabled = @@ -3349,6 +3355,10 @@ public BitSet getGrpcClientErrorStatuses() { return grpcClientErrorStatuses; } + public boolean isCouchbaseInternalSpansEnabled() { + return couchbaseInternalSpansEnabled; + } + public boolean isElasticsearchBodyEnabled() { return elasticsearchBodyEnabled; } @@ -4429,6 +4439,8 @@ public String toString() { + longRunningTraceInitialFlushInterval + ", longRunningTraceFlushInterval=" + longRunningTraceFlushInterval + + ", couchbaseInternalSpansEnabled=" + + couchbaseInternalSpansEnabled + ", elasticsearchBodyEnabled=" + elasticsearchBodyEnabled + ", elasticsearchParamsEnabled="