diff --git a/instrumentation/couchbase/couchbase-3.1/javaagent/couchbase-3.1-javaagent.gradle b/instrumentation/couchbase/couchbase-3.1/javaagent/couchbase-3.1-javaagent.gradle new file mode 100644 index 000000000000..84449d968847 --- /dev/null +++ b/instrumentation/couchbase/couchbase-3.1/javaagent/couchbase-3.1-javaagent.gradle @@ -0,0 +1,20 @@ +apply from: "$rootDir/gradle/instrumentation.gradle" + +muzzle { + pass { + group = "com.couchbase.client" + module = "java-client" + versions = "[3.1,)" + assertInverse = true + } +} + +dependencies { + implementation group: "com.couchbase.client", name: "tracing-opentelemetry", version: "0.3.3" + + library group: "com.couchbase.client", name: "core-io", version: "2.1.0" + + testLibrary group: "com.couchbase.client", name: "java-client", version: "3.1.0" + + testImplementation group: "org.testcontainers", name: "couchbase", version: versions.testcontainers +} \ No newline at end of file diff --git a/instrumentation/couchbase/couchbase-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v3_1/CouchbaseEnvironmentInstrumentation.java b/instrumentation/couchbase/couchbase-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v3_1/CouchbaseEnvironmentInstrumentation.java new file mode 100644 index 000000000000..aef206758d5a --- /dev/null +++ b/instrumentation/couchbase/couchbase-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v3_1/CouchbaseEnvironmentInstrumentation.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.couchbase.v3_1; + +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.couchbase.client.core.env.CoreEnvironment; +import com.couchbase.client.tracing.opentelemetry.OpenTelemetryRequestTracer; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.javaagent.tooling.TypeInstrumentation; +import java.util.Collections; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class CouchbaseEnvironmentInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.couchbase.client.core.env.CoreEnvironment$Builder"); + } + + @Override + public Map, String> transformers() { + return Collections.singletonMap( + isConstructor(), + CouchbaseEnvironmentInstrumentation.class.getName() + "$ConstructorAdvice"); + } + + public static class ConstructorAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.This CoreEnvironment.Builder builder) { + builder.requestTracer( + OpenTelemetryRequestTracer.wrap( + GlobalOpenTelemetry.getTracer("io.opentelemetry.javaagent.couchbase-3.0"))); + } + } +} diff --git a/instrumentation/couchbase/couchbase-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v3_1/CouchbaseInstrumentationModule.java b/instrumentation/couchbase/couchbase-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v3_1/CouchbaseInstrumentationModule.java new file mode 100644 index 000000000000..4948c06f69a1 --- /dev/null +++ b/instrumentation/couchbase/couchbase-3.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/couchbase/v3_1/CouchbaseInstrumentationModule.java @@ -0,0 +1,43 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.couchbase.v3_1; + +import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.ClassLoaderMatcher.hasClassesNamed; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.tooling.InstrumentationModule; +import io.opentelemetry.javaagent.tooling.TypeInstrumentation; +import java.util.Collections; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class CouchbaseInstrumentationModule extends InstrumentationModule { + public CouchbaseInstrumentationModule() { + super("couchbase", "couchbase-3.1"); + } + + @Override + protected String[] additionalHelperClassNames() { + return new String[] { + "com.couchbase.client.tracing.opentelemetry.OpenTelemetryRequestSpan", + "com.couchbase.client.tracing.opentelemetry.OpenTelemetryRequestTracer" + }; + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // New class introduced in 3.1, the minimum version we support. + // NB: Couchbase does not provide any API guarantees on their core IO artifact so reconsider + // instrumenting it instead of each individual JVM artifact if this becomes unmaintainable. + return hasClassesNamed("com.couchbase.client.core.cnc.TracingIdentifiers"); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new CouchbaseEnvironmentInstrumentation()); + } +} diff --git a/instrumentation/couchbase/couchbase-3.1/javaagent/src/test/groovy/CouchbaseClient31Test.groovy b/instrumentation/couchbase/couchbase-3.1/javaagent/src/test/groovy/CouchbaseClient31Test.groovy new file mode 100644 index 000000000000..729a6330cbe8 --- /dev/null +++ b/instrumentation/couchbase/couchbase-3.1/javaagent/src/test/groovy/CouchbaseClient31Test.groovy @@ -0,0 +1,73 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import com.couchbase.client.core.error.DocumentNotFoundException +import com.couchbase.client.java.Cluster +import com.couchbase.client.java.Collection +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import java.time.Duration +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.testcontainers.containers.output.Slf4jLogConsumer +import org.testcontainers.couchbase.BucketDefinition +import org.testcontainers.couchbase.CouchbaseContainer +import org.testcontainers.couchbase.CouchbaseService +import spock.lang.Shared + +// Couchbase instrumentation is owned upstream so we don't assert on the contents of the spans, only +// that the instrumentation is properly registered by the agent, meaning some spans were generated. +class CouchbaseClient31Test extends AgentInstrumentationSpecification { + private static final Logger log = LoggerFactory.getLogger("couchbase-container") + + @Shared + CouchbaseContainer couchbase + @Shared + Cluster cluster + @Shared + Collection collection + + def setupSpec() { + couchbase = new CouchbaseContainer() + .withExposedPorts(8091) + .withEnabledServices(CouchbaseService.KV) + .withBucket(new BucketDefinition("test")) + .withLogConsumer(new Slf4jLogConsumer(log)) + .withStartupTimeout(Duration.ofSeconds(120)) + couchbase.start() + + cluster = Cluster.connect(couchbase.connectionString, couchbase.username, couchbase.password) + def bucket = cluster.bucket("test") + collection = bucket.defaultCollection() + bucket.waitUntilReady(Duration.ofSeconds(10)) + } + + def cleanupSpec() { + couchbase.stop() + } + + def "emits spans"() { + when: + try { + collection.get("id") + } catch (DocumentNotFoundException e) { + // Expected + } + + then: + assertTraces(1) { + trace(0, 2) { + span(0) { + name(~/.*get/) + } + span(1) { + name("dispatch_to_server") + } + } + } + + cleanup: + cluster.disconnect() + } +} diff --git a/settings.gradle b/settings.gradle index 3c49bab31ee2..dd9c0527b24b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -86,6 +86,7 @@ include ':instrumentation:classloaders:javaagent:tomcat-testing' include ':instrumentation:couchbase:couchbase-2.0:javaagent' include ':instrumentation:couchbase:couchbase-2.0:javaagent-unittests' include ':instrumentation:couchbase:couchbase-2.6:javaagent' +include ':instrumentation:couchbase:couchbase-3.1:javaagent' include ':instrumentation:couchbase:couchbase-testing' include ':instrumentation:dropwizard-views-0.7:javaagent' include ':instrumentation:dropwizard-testing'