diff --git a/.fossa.yml b/.fossa.yml index b2374a9de0c9..6301cc302700 100644 --- a/.fossa.yml +++ b/.fossa.yml @@ -91,9 +91,6 @@ targets: - type: gradle path: ./ target: ':instrumentation:camel-2.20:javaagent' - - type: gradle - path: ./ - target: ':instrumentation:clickhouse-client-0.5:javaagent' - type: gradle path: ./ target: ':instrumentation:executors:bootstrap' @@ -394,6 +391,15 @@ targets: - type: gradle path: ./ target: ':instrumentation:cassandra:cassandra-4.4:library' + - type: gradle + path: ./ + target: ':instrumentation:clickhouse:clickhouse-client-common:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:clickhouse:clickhouse-client-v1-0.5:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:clickhouse:clickhouse-client-v2-0.8:javaagent' - type: gradle path: ./ target: ':instrumentation:couchbase:couchbase-2-common:javaagent' diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index a46bc7da2817..e98b6dc2358c 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -51,7 +51,8 @@ These are the supported libraries and frameworks: | [AWS SDK](https://aws.amazon.com/sdk-for-java/) | 1.11 - 1.12.583,
2.2+ | [opentelemetry-aws-sdk-1.11](../instrumentation/aws-sdk/aws-sdk-1.11/library),
[opentelemetry-aws-sdk-1.11-autoconfigure](../instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure),
[opentelemetry-aws-sdk-2.2](../instrumentation/aws-sdk/aws-sdk-2.2/library),
[opentelemetry-aws-sdk-2.2-autoconfigure](../instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure) | [Messaging Spans], [Database Client Spans], [Database Client Metrics] [6], [HTTP Client Spans], [GenAI Client Spans], [GenAI Client Metrics] | | [Azure Core](https://docs.microsoft.com/en-us/java/api/overview/azure/core-readme) | 1.14+ | N/A | Context propagation | | [Cassandra Driver](https://github.com/datastax/java-driver) | 3.0+ | [opentelemetry-cassandra-4.4](../instrumentation/cassandra/cassandra-4.4/library) | [Database Client Spans], [Database Client Metrics] [6] | -| [Clickhouse Client](https://github.com/ClickHouse/clickhouse-java) | 0.5+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | +| [Clickhouse Client V1](https://github.com/ClickHouse/clickhouse-java) | 0.5+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | +| [Clickhouse Client V2](https://github.com/ClickHouse/clickhouse-java) | 0.8+ | N/A | [Database Client Spans], [Database Client Metrics] [6] | | [Couchbase Client](https://github.com/couchbase/couchbase-java-client) | 2.0 - 2.7.x | N/A | [Database Client Spans], [Database Client Metrics] [6] | | [Couchbase Client](https://github.com/couchbase/couchbase-java-client) | 3.1+ | N/A | [Database Client Spans] | | [c3p0](https://github.com/swaldman/c3p0) | 0.9.2+ | [opentelemetry-c3p0-0.9](../instrumentation/c3p0-0.9/library) | [Database Pool Metrics] | diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseScope.java b/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseScope.java deleted file mode 100644 index db779b684954..000000000000 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseScope.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.clickhouse; - -import static io.opentelemetry.javaagent.instrumentation.clickhouse.ClickHouseSingletons.instrumenter; - -import io.opentelemetry.context.Context; -import io.opentelemetry.context.Scope; - -/** Container used to carry state between enter and exit advices */ -public final class ClickHouseScope { - private final ClickHouseDbRequest clickHouseDbRequest; - private final Context context; - private final Scope scope; - - private ClickHouseScope(ClickHouseDbRequest clickHouseDbRequest, Context context, Scope scope) { - this.clickHouseDbRequest = clickHouseDbRequest; - this.context = context; - this.scope = scope; - } - - public static ClickHouseScope start( - Context parentContext, ClickHouseDbRequest clickHouseDbRequest) { - if (!instrumenter().shouldStart(parentContext, clickHouseDbRequest)) { - return null; - } - - Context context = instrumenter().start(parentContext, clickHouseDbRequest); - return new ClickHouseScope(clickHouseDbRequest, context, context.makeCurrent()); - } - - public void end(Throwable throwable) { - scope.close(); - instrumenter().end(context, clickHouseDbRequest, null, throwable); - } -} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseSingletons.java b/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseSingletons.java deleted file mode 100644 index 764d0d2adf14..000000000000 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseSingletons.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.clickhouse; - -import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesExtractor; -import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; -import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; -import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; -import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor; - -public final class ClickHouseSingletons { - - private static final Instrumenter INSTRUMENTER; - - static { - ClickHouseAttributesGetter dbAttributesGetter = new ClickHouseAttributesGetter(); - - INSTRUMENTER = - Instrumenter.builder( - GlobalOpenTelemetry.get(), - "io.opentelemetry.clickhouse-client-0.5", - DbClientSpanNameExtractor.create(dbAttributesGetter)) - .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) - .addAttributesExtractor( - ServerAttributesExtractor.create(new ClickHouseNetworkAttributesGetter())) - .addOperationMetrics(DbClientMetrics.get()) - .buildInstrumenter(SpanKindExtractor.alwaysClient()); - } - - public static Instrumenter instrumenter() { - return INSTRUMENTER; - } - - private ClickHouseSingletons() {} -} diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/build.gradle.kts b/instrumentation/clickhouse/clickhouse-client-common/javaagent/build.gradle.kts new file mode 100644 index 000000000000..afe601decbdc --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +dependencies { + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseAttributesGetter.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java similarity index 82% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseAttributesGetter.java rename to instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java index d185e7227257..0958b3b46224 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseAttributesGetter.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java @@ -3,16 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; -import com.clickhouse.client.ClickHouseException; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; +import java.util.function.Function; import javax.annotation.Nullable; final class ClickHouseAttributesGetter implements DbClientAttributesGetter { + private final Function errorCodeExtractor; + + ClickHouseAttributesGetter(Function errorCodeExtractor) { + this.errorCodeExtractor = errorCodeExtractor; + } + @Nullable @Override public String getDbQueryText(ClickHouseDbRequest request) { @@ -64,9 +70,6 @@ public String getConnectionString(ClickHouseDbRequest request) { @Nullable @Override public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) { - if (error instanceof ClickHouseException) { - return Integer.toString(((ClickHouseException) error).getErrorCode()); - } - return null; + return errorCodeExtractor.apply(error); } } diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseDbRequest.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java similarity index 72% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseDbRequest.java rename to instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java index 1afe12768aa2..35753981cb9b 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseDbRequest.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java @@ -3,12 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; import com.google.auto.value.AutoValue; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementInfo; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlStatementSanitizer; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; +import javax.annotation.Nullable; @AutoValue public abstract class ClickHouseDbRequest { @@ -16,14 +17,18 @@ public abstract class ClickHouseDbRequest { private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled()); - public static ClickHouseDbRequest create(String host, int port, String dbName, String sql) { + public static ClickHouseDbRequest create( + @Nullable String host, @Nullable Integer port, @Nullable String dbName, String sql) { return new AutoValue_ClickHouseDbRequest(host, port, dbName, sanitizer.sanitize(sql)); } + @Nullable public abstract String getHost(); - public abstract int getPort(); + @Nullable + public abstract Integer getPort(); + @Nullable public abstract String getDbName(); public abstract SqlStatementInfo getSqlStatementInfo(); diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseInstrumenterFactory.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseInstrumenterFactory.java new file mode 100644 index 000000000000..7de945c17102 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseInstrumenterFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics; +import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor; +import java.util.function.Function; + +public final class ClickHouseInstrumenterFactory { + + public static Instrumenter createInstrumenter( + String instrumenterName, Function errorCodeExtractor) { + ClickHouseAttributesGetter dbAttributesGetter = + new ClickHouseAttributesGetter(errorCodeExtractor); + + return Instrumenter.builder( + GlobalOpenTelemetry.get(), + instrumenterName, + DbClientSpanNameExtractor.create(dbAttributesGetter)) + .addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) + .addAttributesExtractor( + ServerAttributesExtractor.create(new ClickHouseNetworkAttributesGetter())) + .addOperationMetrics(DbClientMetrics.get()) + .buildInstrumenter(SpanKindExtractor.alwaysClient()); + } + + private ClickHouseInstrumenterFactory() {} +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseNetworkAttributesGetter.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseNetworkAttributesGetter.java similarity index 87% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseNetworkAttributesGetter.java rename to instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseNetworkAttributesGetter.java index 21a57f7f9911..40d5a96176c7 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseNetworkAttributesGetter.java +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseNetworkAttributesGetter.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesGetter; diff --git a/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseScope.java b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseScope.java new file mode 100644 index 000000000000..c9bcfa00fbef --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseScope.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.common; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; + +/** Container used to carry state between enter and exit advices */ +public final class ClickHouseScope { + private final ClickHouseDbRequest clickHouseDbRequest; + private final Context context; + private final Scope scope; + private final Instrumenter instrumenter; + + private ClickHouseScope( + ClickHouseDbRequest clickHouseDbRequest, + Context context, + Scope scope, + Instrumenter instrumenter) { + this.clickHouseDbRequest = clickHouseDbRequest; + this.context = context; + this.scope = scope; + this.instrumenter = instrumenter; + } + + public static ClickHouseScope start( + Instrumenter instrumenter, + Context parentContext, + ClickHouseDbRequest clickHouseDbRequest) { + if (!instrumenter.shouldStart(parentContext, clickHouseDbRequest)) { + return null; + } + + Context context = instrumenter.start(parentContext, clickHouseDbRequest); + return new ClickHouseScope(clickHouseDbRequest, context, context.makeCurrent(), instrumenter); + } + + public void end(Throwable throwable) { + scope.close(); + instrumenter.end(context, clickHouseDbRequest, null, throwable); + } +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/build.gradle.kts b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/build.gradle.kts similarity index 90% rename from instrumentation/clickhouse-client-0.5/javaagent/build.gradle.kts rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/build.gradle.kts index d15e3af9b054..e7cc28a448ea 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/build.gradle.kts +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/build.gradle.kts @@ -12,9 +12,8 @@ muzzle { } dependencies { + implementation(project(":instrumentation:clickhouse:clickhouse-client-common:javaagent")) compileOnly("com.clickhouse:clickhouse-client:0.5.0") - compileOnly("com.google.auto.value:auto-value-annotations") - annotationProcessor("com.google.auto.value:auto-value") testImplementation("com.google.guava:guava") testLibrary("com.clickhouse:clickhouse-client:0.5.0") diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java similarity index 100% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/com/clickhouse/client/ClickHouseRequestAccess.java diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientInstrumentation.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Instrumentation.java similarity index 80% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientInstrumentation.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Instrumentation.java index 0b98a9724ec7..78a3c697f186 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientInstrumentation.java +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Instrumentation.java @@ -3,10 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5.ClickHouseClientV1Singletons.instrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; @@ -16,15 +17,16 @@ import com.clickhouse.client.ClickHouseRequest; import com.clickhouse.client.ClickHouseRequestAccess; import com.clickhouse.client.config.ClickHouseDefaults; -import io.opentelemetry.context.Context; import io.opentelemetry.javaagent.bootstrap.CallDepth; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseScope; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.matcher.ElementMatcher; -public class ClickHouseClientInstrumentation implements TypeInstrumentation { +public class ClickHouseClientV1Instrumentation implements TypeInstrumentation { @Override public ElementMatcher typeMatcher() { return implementsInterface(named("com.clickhouse.client.ClickHouseClient")); @@ -36,11 +38,11 @@ public void transform(TypeTransformer transformer) { isMethod() .and(namedOneOf("executeAndWait", "execute")) .and(takesArgument(0, named("com.clickhouse.client.ClickHouseRequest"))), - this.getClass().getName() + "$ClickHouseExecuteAndWaitAdvice"); + this.getClass().getName() + "$ExecuteAndWaitAdvice"); } @SuppressWarnings("unused") - public static class ClickHouseExecuteAndWaitAdvice { + public static class ExecuteAndWaitAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static ClickHouseScope onEnter( @Advice.Argument(0) ClickHouseRequest clickHouseRequest) { @@ -50,8 +52,6 @@ public static ClickHouseScope onEnter( return null; } - Context parentContext = currentContext(); - ClickHouseDbRequest request = ClickHouseDbRequest.create( clickHouseRequest.getServer().getHost(), @@ -62,7 +62,7 @@ public static ClickHouseScope onEnter( .orElse(ClickHouseDefaults.DATABASE.getDefaultValue().toString()), ClickHouseRequestAccess.getQuery(clickHouseRequest)); - return ClickHouseScope.start(parentContext, request); + return ClickHouseScope.start(instrumenter(), currentContext(), request); } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseInstrumentationModule.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1InstrumentationModule.java similarity index 71% rename from instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseInstrumentationModule.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1InstrumentationModule.java index 28d10ee53894..f45d0c0b891a 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseInstrumentationModule.java +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1InstrumentationModule.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; import static java.util.Collections.singletonList; @@ -14,11 +14,11 @@ import java.util.List; @AutoService(InstrumentationModule.class) -public class ClickHouseInstrumentationModule extends InstrumentationModule +public class ClickHouseClientV1InstrumentationModule extends InstrumentationModule implements ExperimentalInstrumentationModule { - public ClickHouseInstrumentationModule() { - super("clickhouse-client", "clickhouse-client-0.5", "clickhouse"); + public ClickHouseClientV1InstrumentationModule() { + super("clickhouse-client-v1", "clickhouse-client-v1-0.5", "clickhouse", "clickhouse-client"); } @Override @@ -33,6 +33,6 @@ public List injectedClassNames() { @Override public List typeInstrumentations() { - return singletonList(new ClickHouseClientInstrumentation()); + return singletonList(new ClickHouseClientV1Instrumentation()); } } diff --git a/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Singletons.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Singletons.java new file mode 100644 index 000000000000..96615b838c7d --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Singletons.java @@ -0,0 +1,35 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; + +import com.clickhouse.client.ClickHouseException; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseInstrumenterFactory; + +public final class ClickHouseClientV1Singletons { + + private static final String INSTRUMENTER_NAME = "io.opentelemetry.clickhouse-client-v1-0.5"; + private static final Instrumenter INSTRUMENTER; + + static { + INSTRUMENTER = + ClickHouseInstrumenterFactory.createInstrumenter( + INSTRUMENTER_NAME, + error -> { + if (error instanceof ClickHouseException) { + return Integer.toString(((ClickHouseException) error).getErrorCode()); + } + return null; + }); + } + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private ClickHouseClientV1Singletons() {} +} diff --git a/instrumentation/clickhouse-client-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientTest.java b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Test.java similarity index 97% rename from instrumentation/clickhouse-client-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientTest.java rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Test.java index b63258b596f4..53920b0b64f8 100644 --- a/instrumentation/clickhouse-client-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/ClickHouseClientTest.java +++ b/instrumentation/clickhouse/clickhouse-client-v1-0.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv1/v0_5/ClickHouseClientV1Test.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.javaagent.instrumentation.clickhouse; +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv1.v0_5; import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; @@ -47,13 +47,10 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.RegisterExtension; import org.testcontainers.containers.GenericContainer; -@TestInstance(Lifecycle.PER_CLASS) -class ClickHouseClientTest { +class ClickHouseClientV1Test { @RegisterExtension private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); @@ -69,7 +66,7 @@ class ClickHouseClientTest { private static ClickHouseClient client; @BeforeAll - void setup() throws ClickHouseException { + static void setup() throws ClickHouseException { clickhouseServer.start(); port = clickhouseServer.getMappedPort(8123); host = clickhouseServer.getHost(); @@ -89,7 +86,7 @@ void setup() throws ClickHouseException { } @AfterAll - void cleanup() { + static void cleanup() { if (client != null) { client.close(); } @@ -122,7 +119,7 @@ void testConnectionStringWithoutDatabaseSpecifiedStillGeneratesSpans() assertDurationMetric( testing, - "io.opentelemetry.clickhouse-client-0.5", + "io.opentelemetry.clickhouse-client-v1-0.5", DB_SYSTEM_NAME, DB_OPERATION_NAME, DB_NAMESPACE, diff --git a/instrumentation/clickhouse-client-0.5/metadata.yaml b/instrumentation/clickhouse/clickhouse-client-v1-0.5/metadata.yaml similarity index 100% rename from instrumentation/clickhouse-client-0.5/metadata.yaml rename to instrumentation/clickhouse/clickhouse-client-v1-0.5/metadata.yaml diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/build.gradle.kts b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/build.gradle.kts new file mode 100644 index 000000000000..2e447b080192 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/build.gradle.kts @@ -0,0 +1,37 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("com.clickhouse") + module.set("client-v2") + versions.set("[0.6.4,)") + assertInverse.set(true) + } +} + +dependencies { + implementation(project(":instrumentation:clickhouse:clickhouse-client-common:javaagent")) + library("com.clickhouse:client-v2:0.8.0") +} + +val collectMetadata = findProperty("collectMetadata")?.toString() ?: "false" + +tasks { + test { + usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) + systemProperty("collectMetadata", collectMetadata) + } + + val testStableSemconv by registering(Test::class) { + jvmArgs("-Dotel.semconv-stability.opt-in=database") + + systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database") + systemProperty("collectMetadata", collectMetadata) + } + + check { + dependsOn(testStableSemconv) + } +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Instrumentation.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Instrumentation.java new file mode 100644 index 000000000000..3255589460fe --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Instrumentation.java @@ -0,0 +1,90 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; +import static io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8.ClickHouseClientV2Singletons.instrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.isSubTypeOf; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import com.clickhouse.client.api.Client; +import com.clickhouse.client.api.query.QuerySettings; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.semconv.network.internal.AddressAndPort; +import io.opentelemetry.javaagent.bootstrap.CallDepth; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseScope; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class ClickHouseClientV2Instrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return named("com.clickhouse.client.api.Client"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(isPublic()) + .and(named("query")) + .and(takesArgument(0, String.class)) + .and(takesArgument(1, isSubTypeOf(Map.class))) + .and(takesArgument(2, named("com.clickhouse.client.api.query.QuerySettings"))), + this.getClass().getName() + "$QueryAdvice"); + } + + @SuppressWarnings("unused") + public static class QueryAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ClickHouseScope onEnter( + @Advice.This Client client, + @Advice.Argument(0) String sqlQuery, + @Advice.Argument(1) Map queryParams, + @Advice.Argument(2) QuerySettings querySettings) { + CallDepth callDepth = CallDepth.forClass(Client.class); + if (callDepth.getAndIncrement() > 0 || sqlQuery == null) { + return null; + } + + // https://clickhouse.com/docs/integrations/language-clients/java/client#client-configuration + // Currently, clientv2 supports only one endpoint. Since the endpoint is not going to change + // we'll cache it in a virtual field. + AddressAndPort addressAndPort = ClickHouseClientV2Singletons.getAddressAndPort(client); + if (addressAndPort == null) { + String endpoint = client.getEndpoints().stream().findFirst().orElse(null); + addressAndPort = ClickHouseClientV2Singletons.setAddressAndPort(client, endpoint); + } + + String database = client.getConfiguration().get("database"); + Context parentContext = currentContext(); + ClickHouseDbRequest request = + ClickHouseDbRequest.create( + addressAndPort.getAddress(), addressAndPort.getPort(), database, sqlQuery); + + return ClickHouseScope.start(instrumenter(), parentContext, request); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Thrown Throwable throwable, @Advice.Enter ClickHouseScope scope) { + CallDepth callDepth = CallDepth.forClass(Client.class); + if (callDepth.decrementAndGet() > 0 || scope == null) { + return; + } + + scope.end(throwable); + } + } +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2InstrumentationModule.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2InstrumentationModule.java new file mode 100644 index 000000000000..d08b758e6d68 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2InstrumentationModule.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import static java.util.Collections.singletonList; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public class ClickHouseClientV2InstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { + + public ClickHouseClientV2InstrumentationModule() { + super("clickhouse-client-v2", "clickhouse-client-v2-0.8", "clickhouse", "clickhouse-client"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new ClickHouseClientV2Instrumentation()); + } +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Singletons.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Singletons.java new file mode 100644 index 000000000000..9d24b12a0378 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Singletons.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import com.clickhouse.client.api.Client; +import com.clickhouse.client.api.ServerException; +import io.opentelemetry.instrumentation.api.incubator.semconv.net.internal.UrlParser; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.semconv.network.internal.AddressAndPort; +import io.opentelemetry.instrumentation.api.util.VirtualField; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseDbRequest; +import io.opentelemetry.javaagent.instrumentation.clickhouse.common.ClickHouseInstrumenterFactory; + +public final class ClickHouseClientV2Singletons { + + private static final String INSTRUMENTER_NAME = "io.opentelemetry.clickhouse-client-v2-0.8"; + private static final Instrumenter INSTRUMENTER; + + static { + INSTRUMENTER = + ClickHouseInstrumenterFactory.createInstrumenter( + INSTRUMENTER_NAME, + error -> { + if (error instanceof ServerException) { + return Integer.toString(((ServerException) error).getCode()); + } + return null; + }); + } + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private static final VirtualField ADDRESS_AND_PORT = + VirtualField.find(Client.class, AddressAndPort.class); + + public static AddressAndPort getAddressAndPort(Client client) { + return ADDRESS_AND_PORT.get(client); + } + + public static AddressAndPort setAddressAndPort(Client client, String endpoint) { + AddressAndPort addressAndPort = new AddressAndPort(); + + if (endpoint != null) { + addressAndPort.setAddress(UrlParser.getHost(endpoint)); + addressAndPort.setPort(UrlParser.getPort(endpoint)); + } + ADDRESS_AND_PORT.set(client, addressAndPort); + + return addressAndPort; + } + + private ClickHouseClientV2Singletons() {} +} diff --git a/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Test.java b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Test.java new file mode 100644 index 000000000000..8b3573ed6494 --- /dev/null +++ b/instrumentation/clickhouse/clickhouse-client-v2-0.8/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/clickhouse/clientv2/v0_8/ClickHouseClientV2Test.java @@ -0,0 +1,343 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.clickhouse.clientv2.v0_8; + +import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; +import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE; +import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; +import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; +import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; +import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +import com.clickhouse.client.api.Client; +import com.clickhouse.client.api.ServerException; +import com.clickhouse.client.api.command.CommandResponse; +import com.clickhouse.client.api.enums.Protocol; +import com.clickhouse.client.api.query.GenericRecord; +import com.clickhouse.client.api.query.QueryResponse; +import com.clickhouse.client.api.query.QuerySettings; +import com.clickhouse.client.api.query.Records; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.SemconvStability; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.testcontainers.containers.GenericContainer; + +class ClickHouseClientV2Test { + + @RegisterExtension + private static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + private static final GenericContainer clickhouseServer = + new GenericContainer<>("clickhouse/clickhouse-server:24.4.2").withExposedPorts(8123); + + private static final String dbName = "default"; + private static final String tableName = "test_table"; + private static int port; + private static String host; + private static Client client; + private static final String username = "default"; + private static final String password = ""; + + @BeforeAll + static void setup() throws Exception { + clickhouseServer.start(); + port = clickhouseServer.getMappedPort(8123); + host = clickhouseServer.getHost(); + + client = + new Client.Builder() + .addEndpoint(Protocol.HTTP, host, port, false) + .setDefaultDatabase(dbName) + .setUsername(username) + .setPassword(password) + .setOption("compress", "false") + .build(); + + QueryResponse response = + client + .query("create table if not exists " + tableName + "(value String) engine=Memory") + .get(); + response.close(); + + // wait for CREATE operation and clear + testing.waitForTraces(1); + testing.clearData(); + } + + @AfterAll + static void cleanup() { + if (client != null) { + client.close(); + } + clickhouseServer.stop(); + } + + @Test + void testConnectionStringWithoutDatabaseSpecifiedStillGeneratesSpans() throws Exception { + Client client = + new Client.Builder() + .addEndpoint(Protocol.HTTP, host, port, false) + .setOption("compress", "false") + .setUsername(username) + .setPassword(password) + .build(); + + QueryResponse response = client.query("select * from " + tableName).get(); + response.close(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasNoParent() + .hasAttributesSatisfyingExactly( + attributeAssertions("select * from " + tableName, "SELECT")))); + + assertDurationMetric( + testing, + "io.opentelemetry.clickhouse-clientv2-0.8", + DB_SYSTEM_NAME, + DB_OPERATION_NAME, + DB_NAMESPACE, + SERVER_ADDRESS, + SERVER_PORT); + } + + @Test + void testQueryWithStringQuery() throws Exception { + testing.runWithSpan( + "parent", + () -> { + QueryResponse response = + client.query("insert into " + tableName + " values('1')('2')('3')").get(); + response.close(); + + response = client.query("select * from " + tableName).get(); + response.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("INSERT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "insert into " + tableName + " values(?)(?)(?)", "INSERT")), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions("select * from " + tableName, "SELECT")))); + } + + @Test + void testQueryWithStringQueryAndId() throws Exception { + testing.runWithSpan( + "parent", + () -> { + QuerySettings querySettings = new QuerySettings(); + querySettings.setQueryId("test_query_id"); + + QueryResponse response = client.query("select * from " + tableName, querySettings).get(); + response.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions("select * from " + tableName, "SELECT")))); + } + + @Test + void testQueryThrowsServerException() { + Throwable thrown = + catchThrowable( + () -> { + QueryResponse response = client.query("select * from non_existent_table").get(); + response.close(); + }); + + assertThat(thrown).isInstanceOf(ServerException.class); + + List assertions = + new ArrayList<>(attributeAssertions("select * from non_existent_table", "SELECT")); + if (SemconvStability.emitStableDatabaseSemconv()) { + assertions.add(equalTo(DB_RESPONSE_STATUS_CODE, "60")); + assertions.add(equalTo(ERROR_TYPE, "com.clickhouse.client.api.ServerException")); + } + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfyingExactly(assertions))); + } + + @Test + void testSendQuery() throws Exception { + testing.runWithSpan( + "parent", + () -> { + CompletableFuture future = + client.execute("select * from " + tableName + " limit 1"); + CommandResponse results = future.get(); + assertThat(results.getReadRows()).isEqualTo(0); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " limit ?", "SELECT")))); + } + + @Test + void testSendQueryAll() { + testing.runWithSpan( + "parent", + () -> { + List records = client.queryAll("select * from " + tableName + " limit 1"); + assertThat(records.size()).isEqualTo(0); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " limit ?", "SELECT")))); + } + + @Test + void testSendQueryRecords() throws Exception { + testing.runWithSpan( + "parent", + () -> { + Records records = + client.queryRecords("insert into " + tableName + " values('test_value')").get(); + records.close(); + + records = client.queryRecords("select * from " + tableName + " limit 1").get(); + records.close(); + assertThat(records.getReadRows()).isEqualTo(1); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("INSERT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "insert into " + tableName + " values(?)", "INSERT")), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " limit ?", "SELECT")))); + } + + @Test + void testPlaceholderQuery() throws Exception { + Map queryParams = new HashMap<>(); + queryParams.put("param_s", Instant.now().getEpochSecond()); + + testing.runWithSpan( + "parent", + () -> { + QueryResponse response = + client + .query( + "select * from " + tableName + " where value={param_s: String}", + queryParams, + null) + .get(); + response.close(); + }); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasNoParent().hasAttributes(Attributes.empty()), + span -> + span.hasName("SELECT " + dbName) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasAttributesSatisfyingExactly( + attributeAssertions( + "select * from " + tableName + " where value={param_s: String}", + "SELECT")))); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + private static List attributeAssertions(String statement, String operation) { + return asList( + equalTo(maybeStable(DB_SYSTEM), DbIncubatingAttributes.DbSystemIncubatingValues.CLICKHOUSE), + equalTo(maybeStable(DB_NAME), dbName), + equalTo(SERVER_ADDRESS, host), + equalTo(SERVER_PORT, port), + equalTo(maybeStable(DB_STATEMENT), statement), + equalTo(maybeStable(DB_OPERATION), operation)); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 511735605e5c..0baae4bb6962 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -193,7 +193,9 @@ include(":instrumentation:cassandra:cassandra-4.4:library") include(":instrumentation:cassandra:cassandra-4.4:testing") include(":instrumentation:cassandra:cassandra-4-common:testing") include(":instrumentation:cdi-testing") -include(":instrumentation:clickhouse-client-0.5:javaagent") +include(":instrumentation:clickhouse:clickhouse-client-common:javaagent") +include(":instrumentation:clickhouse:clickhouse-client-v1-0.5:javaagent") +include(":instrumentation:clickhouse:clickhouse-client-v2-0.8:javaagent") include(":instrumentation:couchbase:couchbase-2.0:javaagent") include(":instrumentation:couchbase:couchbase-2.6:javaagent") include(":instrumentation:couchbase:couchbase-2-common:javaagent")