-
Notifications
You must be signed in to change notification settings - Fork 967
Support tracing the ClickHouse Java HTTP client #11660
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
5193f8f
67d58e0
5d8d8cd
0189967
4460d7f
e165858
d212f56
af67e52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
plugins { | ||
id("otel.javaagent-instrumentation") | ||
} | ||
|
||
muzzle { | ||
pass { | ||
group.set("com.clickhouse.client") | ||
module.set("clickhouse-client") | ||
versions.set("[0.5.0,)") | ||
assertInverse.set(true) | ||
} | ||
} | ||
|
||
dependencies { | ||
compileOnly("com.clickhouse:clickhouse-client:0.5.0") | ||
compileOnly("com.google.auto.value:auto-value-annotations") | ||
annotationProcessor("com.google.auto.value:auto-value") | ||
|
||
testLibrary("com.clickhouse:clickhouse-client:0.5.0") | ||
testLibrary("com.clickhouse:clickhouse-http-client:0.5.0") | ||
testLibrary("org.apache.httpcomponents.client5:httpclient5:5.2.3") | ||
} | ||
|
||
tasks { | ||
test { | ||
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.clickhouse; | ||
|
||
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; | ||
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; | ||
import javax.annotation.Nullable; | ||
|
||
final class ClickHouseAttributesGetter implements DbClientAttributesGetter<ClickHouseDbRequest> { | ||
@Nullable | ||
@Override | ||
public String getStatement(ClickHouseDbRequest request) { | ||
if (request.getSqlStatementInfo() == null) { | ||
return null; | ||
} | ||
return request.getSqlStatementInfo().getFullStatement(); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public String getOperation(ClickHouseDbRequest request) { | ||
if (request.getSqlStatementInfo() == null) { | ||
return null; | ||
} | ||
return request.getSqlStatementInfo().getOperation(); | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public String getSystem(ClickHouseDbRequest request) { | ||
return DbIncubatingAttributes.DbSystemValues.CLICKHOUSE; | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public String getUser(ClickHouseDbRequest request) { | ||
return null; | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public String getName(ClickHouseDbRequest request) { | ||
String dbName = request.getDbName(); | ||
if (dbName == null || dbName.isEmpty()) { | ||
return null; | ||
} | ||
return dbName; | ||
} | ||
|
||
@Nullable | ||
@Override | ||
public String getConnectionString(ClickHouseDbRequest request) { | ||
return null; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.clickhouse; | ||
|
||
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; | ||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; | ||
import static io.opentelemetry.javaagent.instrumentation.clickhouse.ClickHouseSingletons.instrumenter; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument; | ||
|
||
import com.clickhouse.client.ClickHouseClient; | ||
import com.clickhouse.client.ClickHouseRequest; | ||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.context.Scope; | ||
import io.opentelemetry.javaagent.bootstrap.CallDepth; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
public class ClickHouseClientInstrumentation implements TypeInstrumentation { | ||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return implementsInterface(named("com.clickhouse.client.ClickHouseClient")); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
isMethod() | ||
.and(namedOneOf("executeAndWait", "execute")) | ||
.and(takesArgument(0, named("com.clickhouse.client.ClickHouseRequest"))), | ||
this.getClass().getName() + "$ClickHouseExecuteAndWaitAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class ClickHouseExecuteAndWaitAdvice { | ||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void onEnter( | ||
@Advice.Argument(0) ClickHouseRequest<?> clickHouseRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope, | ||
@Advice.Local("otelCallDepth") CallDepth callDepth) { | ||
|
||
callDepth = CallDepth.forClass(ClickHouseClient.class); | ||
if (callDepth.getAndIncrement() > 0) { | ||
return; | ||
} | ||
|
||
if (clickHouseRequest == null) { | ||
jaydeluca marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return; | ||
} | ||
|
||
Context parentContext = currentContext(); | ||
|
||
ClickHouseDbRequest request = | ||
ClickHouseDbRequest.create( | ||
clickHouseRequest.getServer().getHost(), | ||
clickHouseRequest.getServer().getPort(), | ||
clickHouseRequest.getServer().getDatabase().get(), | ||
clickHouseRequest.getPreparedQuery().getOriginalQuery()); | ||
|
||
if (!instrumenter().shouldStart(parentContext, request)) { | ||
return; | ||
} | ||
|
||
context = instrumenter().start(parentContext, request); | ||
scope = context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void onExit( | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Local("otelRequest") ClickHouseDbRequest clickHouseRequest, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope, | ||
@Advice.Local("otelCallDepth") CallDepth callDepth) { | ||
|
||
if (callDepth.decrementAndGet() > 0) { | ||
return; | ||
} | ||
|
||
if (scope == null) { | ||
return; | ||
} | ||
|
||
scope.close(); | ||
instrumenter().end(context, clickHouseRequest, null, throwable); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.clickhouse; | ||
|
||
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.CommonConfig; | ||
|
||
@AutoValue | ||
public abstract class ClickHouseDbRequest { | ||
|
||
private static final SqlStatementSanitizer sanitizer = | ||
SqlStatementSanitizer.create(CommonConfig.get().isStatementSanitizationEnabled()); | ||
|
||
public static ClickHouseDbRequest create(String host, Integer port, String dbName, String sql) { | ||
return new AutoValue_ClickHouseDbRequest(host, port, dbName, sanitizer.sanitize(sql)); | ||
} | ||
|
||
public abstract String getHost(); | ||
|
||
public abstract Integer getPort(); | ||
|
||
public abstract String getDbName(); | ||
|
||
public abstract SqlStatementInfo getSqlStatementInfo(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.clickhouse; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
@AutoService(InstrumentationModule.class) | ||
public class ClickHouseInstrumentationModule extends InstrumentationModule { | ||
|
||
public ClickHouseInstrumentationModule() { | ||
super("clickhouse", "clickhouse-client"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. According to previous convention, the module should be named to |
||
} | ||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return Collections.singletonList(new ClickHouseClientInstrumentation()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggest to use static import here. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.clickhouse; | ||
|
||
import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesGetter; | ||
|
||
final class ClickHouseNetworkAttributesGetter | ||
implements ServerAttributesGetter<ClickHouseDbRequest> { | ||
|
||
@Override | ||
public String getServerAddress(ClickHouseDbRequest request) { | ||
return request.getHost(); | ||
} | ||
|
||
@Override | ||
public Integer getServerPort(ClickHouseDbRequest request) { | ||
return request.getPort(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* 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.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<ClickHouseDbRequest, Void> INSTRUMENTER; | ||
|
||
static { | ||
ClickHouseAttributesGetter dbAttributesGetter = new ClickHouseAttributesGetter(); | ||
|
||
INSTRUMENTER = | ||
Instrumenter.<ClickHouseDbRequest, Void>builder( | ||
GlobalOpenTelemetry.get(), | ||
"io.opentelemetry.clickhouse-client", | ||
DbClientSpanNameExtractor.create(dbAttributesGetter)) | ||
.addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter)) | ||
.addAttributesExtractor( | ||
ServerAttributesExtractor.create(new ClickHouseNetworkAttributesGetter())) | ||
.buildInstrumenter(SpanKindExtractor.alwaysClient()); | ||
} | ||
|
||
public static Instrumenter<ClickHouseDbRequest, Void> instrumenter() { | ||
return INSTRUMENTER; | ||
} | ||
|
||
private ClickHouseSingletons() {} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.