Skip to content

Commit 7f8e5ec

Browse files
traskjaydeluca
andauthored
Simplify ClickHouse instrumentation (#16206)
Co-authored-by: Jay DeLuca <jaydeluca4@gmail.com>
1 parent 0b15b77 commit 7f8e5ec

8 files changed

Lines changed: 159 additions & 82 deletions

File tree

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractor.java

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ public static <REQUEST> SpanNameExtractor<REQUEST> create(
3030
return new SqlClientSpanNameExtractor<>(getter);
3131
}
3232

33+
/**
34+
* Returns a {@link SpanNameExtractor} that uses SQL parsing for stable semconv span names but
35+
* preserves the old semconv span name format (without collection name) for backward
36+
* compatibility.
37+
*
38+
* <p>This is a transitional method for migrating instrumentations from {@link
39+
* DbClientAttributesGetter} to {@link SqlClientAttributesGetter}. Once old database semconv are
40+
* dropped, callers should switch to {@link #create(SqlClientAttributesGetter)}.
41+
*
42+
* @deprecated Use {@link #create(SqlClientAttributesGetter)} instead.
43+
*/
44+
@Deprecated // to be removed in 3.0
45+
public static <REQUEST> SpanNameExtractor<REQUEST> createForMigration(
46+
SqlClientAttributesGetter<REQUEST, ?> getter) {
47+
return new MigratingSqlClientSpanNameExtractor<>(getter);
48+
}
49+
3350
private static final String DEFAULT_SPAN_NAME = "DB Query";
3451

3552
private DbClientSpanNameExtractor() {}
@@ -166,9 +183,15 @@ public String extract(REQUEST request) {
166183

167184
if (rawQueryTexts.isEmpty()) {
168185
if (SemconvStability.emitStableDatabaseSemconv()) {
169-
return computeSpanNameStable(getter, request, null, null, null);
186+
String querySummary = getter.getDbQuerySummary(request);
187+
if (querySummary != null) {
188+
return querySummary;
189+
}
190+
String operationName = getter.getDbOperationName(request);
191+
return computeSpanNameStable(getter, request, operationName, null, null);
170192
}
171-
return computeSpanName(namespace, null, null, null);
193+
String operationName = getter.getDbOperationName(request);
194+
return computeSpanName(namespace, operationName, null, null);
172195
}
173196

174197
if (!SemconvStability.emitStableDatabaseSemconv()) {
@@ -210,4 +233,42 @@ private boolean isBatch(REQUEST request) {
210233
return batchSize != null && batchSize > 1;
211234
}
212235
}
236+
237+
/**
238+
* A transitional span name extractor that uses SQL parsing for stable semconv but preserves the
239+
* old semconv span name format (operation + namespace, without collection name) for backward
240+
* compatibility during migration from {@link DbClientAttributesGetter} to {@link
241+
* SqlClientAttributesGetter}.
242+
*/
243+
private static final class MigratingSqlClientSpanNameExtractor<REQUEST>
244+
extends DbClientSpanNameExtractor<REQUEST> {
245+
246+
private final SqlClientAttributesGetter<REQUEST, ?> getter;
247+
private final SqlClientSpanNameExtractor<REQUEST> sqlDelegate;
248+
249+
private MigratingSqlClientSpanNameExtractor(SqlClientAttributesGetter<REQUEST, ?> getter) {
250+
this.getter = getter;
251+
this.sqlDelegate = new SqlClientSpanNameExtractor<>(getter);
252+
}
253+
254+
@Override
255+
public String extract(REQUEST request) {
256+
if (SemconvStability.emitStableDatabaseSemconv()) {
257+
return sqlDelegate.extract(request);
258+
}
259+
// For old semconv, use the generic span name format (operation + namespace)
260+
// without collection name to preserve backward compatibility
261+
String namespace = getter.getDbNamespace(request);
262+
Collection<String> rawQueryTexts = getter.getRawQueryTexts(request);
263+
String operationName = null;
264+
if (rawQueryTexts.size() == 1) {
265+
SqlQuery sanitizedQuery = SqlQuerySanitizerUtil.sanitize(rawQueryTexts.iterator().next());
266+
operationName = sanitizedQuery.getOperationName();
267+
}
268+
if (operationName == null) {
269+
operationName = getter.getDbOperationName(request);
270+
}
271+
return computeSpanName(namespace, operationName, null, null);
272+
}
273+
}
213274
}

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractor.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,13 @@ public static <REQUEST, RESPONSE> SqlClientAttributesExtractorBuilder<REQUEST, R
5757
private final SqlClientAttributesGetter<REQUEST, RESPONSE> getter;
5858
private final InternalNetworkAttributesExtractor<REQUEST, RESPONSE> internalNetworkExtractor;
5959
private final ServerAttributesExtractor<REQUEST, RESPONSE> serverAttributesExtractor;
60-
private final AttributeKey<String> oldSemconvTableAttribute;
60+
@Nullable private final AttributeKey<String> oldSemconvTableAttribute;
6161
private final boolean querySanitizationEnabled;
6262
private final boolean captureQueryParameters;
6363

6464
SqlClientAttributesExtractor(
6565
SqlClientAttributesGetter<REQUEST, RESPONSE> getter,
66-
AttributeKey<String> oldSemconvTableAttribute,
66+
@Nullable AttributeKey<String> oldSemconvTableAttribute,
6767
boolean querySanitizationEnabled,
6868
boolean captureQueryParameters) {
6969
this.getter = getter;
@@ -91,7 +91,9 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST
9191
attributes.put(
9292
DB_STATEMENT, querySanitizationEnabled ? sanitizedQuery.getQueryText() : rawQueryText);
9393
attributes.put(DB_OPERATION, operationName);
94-
attributes.put(oldSemconvTableAttribute, sanitizedQuery.getCollectionName());
94+
if (oldSemconvTableAttribute != null) {
95+
attributes.put(oldSemconvTableAttribute, sanitizedQuery.getCollectionName());
96+
}
9597
}
9698
}
9799

instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlClientAttributesExtractorBuilder.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,10 @@
55

66
package io.opentelemetry.instrumentation.api.incubator.semconv.db;
77

8-
import static java.util.Objects.requireNonNull;
9-
108
import com.google.errorprone.annotations.CanIgnoreReturnValue;
119
import io.opentelemetry.api.common.AttributeKey;
1210
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
11+
import javax.annotation.Nullable;
1312

1413
/** A builder of {@link SqlClientAttributesExtractor}. */
1514
public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
@@ -18,7 +17,7 @@ public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
1817
private static final AttributeKey<String> DB_SQL_TABLE = AttributeKey.stringKey("db.sql.table");
1918

2019
final SqlClientAttributesGetter<REQUEST, RESPONSE> getter;
21-
AttributeKey<String> oldSemconvTableAttribute = DB_SQL_TABLE;
20+
@Nullable AttributeKey<String> oldSemconvTableAttribute = DB_SQL_TABLE;
2221
boolean querySanitizationEnabled = true;
2322
boolean captureQueryParameters = false;
2423

@@ -27,13 +26,16 @@ public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
2726
}
2827

2928
/**
29+
* Sets the attribute key for the old semconv table attribute. Pass {@code null} to disable
30+
* emitting any table attribute under old semconv.
31+
*
3032
* @deprecated new semantic conventions always use db.collection.name
3133
*/
3234
@CanIgnoreReturnValue
3335
@Deprecated // to be removed in 3.0
3436
public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setTableAttribute(
35-
AttributeKey<String> oldSemconvTableAttribute) {
36-
this.oldSemconvTableAttribute = requireNonNull(oldSemconvTableAttribute);
37+
@Nullable AttributeKey<String> oldSemconvTableAttribute) {
38+
this.oldSemconvTableAttribute = oldSemconvTableAttribute;
3739
return this;
3840
}
3941

instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/DbClientSpanNameExtractorTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package io.opentelemetry.instrumentation.api.incubator.semconv.db;
77

88
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv;
9+
import static java.util.Collections.emptyList;
910
import static java.util.Collections.singleton;
1011
import static org.assertj.core.api.Assertions.assertThat;
1112
import static org.mockito.Mockito.lenient;
@@ -208,5 +209,64 @@ void shouldExtractFullSpanNameForSingleQueryBatch() {
208209
: "INSERT database.table");
209210
}
210211

212+
@Test
213+
void shouldFallBackToExplicitOperationNameForEmptySqlQuery() {
214+
// given
215+
DbRequest dbRequest = new DbRequest();
216+
217+
when(sqlAttributesGetter.getRawQueryTexts(dbRequest)).thenReturn(emptyList());
218+
when(sqlAttributesGetter.getDbOperationName(dbRequest)).thenReturn("WRITE");
219+
when(sqlAttributesGetter.getDbNamespace(dbRequest)).thenReturn("mydb");
220+
221+
SpanNameExtractor<DbRequest> underTest = DbClientSpanNameExtractor.create(sqlAttributesGetter);
222+
223+
// when
224+
String spanName = underTest.extract(dbRequest);
225+
226+
// then
227+
assertThat(spanName).isEqualTo("WRITE mydb");
228+
}
229+
230+
@Test
231+
@SuppressWarnings("deprecation") // testing deprecated method
232+
void shouldPreserveOldSemconvSpanNameForMigration() {
233+
// given
234+
DbRequest dbRequest = new DbRequest();
235+
236+
when(sqlAttributesGetter.getRawQueryTexts(dbRequest))
237+
.thenReturn(singleton("SELECT * from table"));
238+
lenient().when(sqlAttributesGetter.getDbNamespace(dbRequest)).thenReturn("database");
239+
240+
SpanNameExtractor<DbRequest> underTest =
241+
DbClientSpanNameExtractor.createForMigration(sqlAttributesGetter);
242+
243+
// when
244+
String spanName = underTest.extract(dbRequest);
245+
246+
// then
247+
assertThat(spanName)
248+
.isEqualTo(emitStableDatabaseSemconv() ? "SELECT table" : "SELECT database");
249+
}
250+
251+
@Test
252+
@SuppressWarnings("deprecation") // testing deprecated method
253+
void shouldFallBackToExplicitOperationForEmptySqlQueryInMigration() {
254+
// given
255+
DbRequest dbRequest = new DbRequest();
256+
257+
when(sqlAttributesGetter.getRawQueryTexts(dbRequest)).thenReturn(emptyList());
258+
when(sqlAttributesGetter.getDbOperationName(dbRequest)).thenReturn("WRITE");
259+
when(sqlAttributesGetter.getDbNamespace(dbRequest)).thenReturn("mydb");
260+
261+
SpanNameExtractor<DbRequest> underTest =
262+
DbClientSpanNameExtractor.createForMigration(sqlAttributesGetter);
263+
264+
// when
265+
String spanName = underTest.extract(dbRequest);
266+
267+
// then
268+
assertThat(spanName).isEqualTo("WRITE mydb");
269+
}
270+
211271
static class DbRequest {}
212272
}

instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseAttributesGetter.java

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,48 +5,25 @@
55

66
package io.opentelemetry.javaagent.instrumentation.clickhouse.common;
77

8-
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter;
8+
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter;
99
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
10+
import java.util.Collection;
11+
import java.util.Collections;
1012
import java.util.function.Function;
1113
import javax.annotation.Nullable;
1214

1315
final class ClickHouseAttributesGetter
14-
implements DbClientAttributesGetter<ClickHouseDbRequest, Void> {
16+
implements SqlClientAttributesGetter<ClickHouseDbRequest, Void> {
1517

1618
private final Function<Throwable, String> errorCodeExtractor;
1719

1820
ClickHouseAttributesGetter(Function<Throwable, String> errorCodeExtractor) {
1921
this.errorCodeExtractor = errorCodeExtractor;
2022
}
2123

22-
@Nullable
23-
@Override
24-
public String getDbQueryText(ClickHouseDbRequest request) {
25-
if (request.getSqlQueryWithSummary() != null) {
26-
return request.getSqlQueryWithSummary().getQueryText();
27-
}
28-
if (request.getSqlQuery() != null) {
29-
return request.getSqlQuery().getQueryText();
30-
}
31-
return null;
32-
}
33-
34-
@Nullable
3524
@Override
36-
public String getDbOperationName(ClickHouseDbRequest request) {
37-
if (request.getSqlQuery() != null) {
38-
return request.getSqlQuery().getOperationName();
39-
}
40-
return null;
41-
}
42-
43-
@Nullable
44-
@Override
45-
public String getDbQuerySummary(ClickHouseDbRequest request) {
46-
if (request.getSqlQueryWithSummary() != null) {
47-
return request.getSqlQueryWithSummary().getQuerySummary();
48-
}
49-
return null;
25+
public Collection<String> getRawQueryTexts(ClickHouseDbRequest request) {
26+
return Collections.singletonList(request.getSql());
5027
}
5128

5229
@Override
@@ -69,4 +46,14 @@ public String getDbNamespace(ClickHouseDbRequest request) {
6946
public String getDbResponseStatusCode(@Nullable Void response, @Nullable Throwable error) {
7047
return errorCodeExtractor.apply(error);
7148
}
49+
50+
@Override
51+
public String getServerAddress(ClickHouseDbRequest request) {
52+
return request.getHost();
53+
}
54+
55+
@Override
56+
public Integer getServerPort(ClickHouseDbRequest request) {
57+
return request.getPort();
58+
}
7259
}

instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseDbRequest.java

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,14 @@
66
package io.opentelemetry.javaagent.instrumentation.clickhouse.common;
77

88
import com.google.auto.value.AutoValue;
9-
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlQuery;
10-
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlQuerySanitizer;
11-
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
12-
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
139
import javax.annotation.Nullable;
1410

1511
@AutoValue
1612
public abstract class ClickHouseDbRequest {
1713

18-
private static final SqlQuerySanitizer sanitizer =
19-
SqlQuerySanitizer.create(AgentCommonConfig.get().isQuerySanitizationEnabled());
20-
2114
public static ClickHouseDbRequest create(
2215
@Nullable String host, @Nullable Integer port, @Nullable String namespace, String sql) {
23-
SqlQuery sqlQuery = SemconvStability.emitOldDatabaseSemconv() ? sanitizer.sanitize(sql) : null;
24-
SqlQuery sqlQueryWithSummary =
25-
SemconvStability.emitStableDatabaseSemconv() ? sanitizer.sanitizeWithSummary(sql) : null;
26-
return new AutoValue_ClickHouseDbRequest(host, port, namespace, sqlQuery, sqlQueryWithSummary);
16+
return new AutoValue_ClickHouseDbRequest(host, port, namespace, sql);
2717
}
2818

2919
@Nullable
@@ -35,9 +25,5 @@ public static ClickHouseDbRequest create(
3525
@Nullable
3626
public abstract String getNamespace();
3727

38-
@Nullable
39-
public abstract SqlQuery getSqlQuery();
40-
41-
@Nullable
42-
public abstract SqlQuery getSqlQueryWithSummary();
28+
public abstract String getSql();
4329
}

instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseInstrumenterFactory.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,16 @@
66
package io.opentelemetry.javaagent.instrumentation.clickhouse.common;
77

88
import io.opentelemetry.api.GlobalOpenTelemetry;
9-
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesExtractor;
109
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientMetrics;
1110
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientSpanNameExtractor;
11+
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesExtractor;
1212
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1313
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor;
14-
import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor;
1514
import java.util.function.Function;
1615

1716
public final class ClickHouseInstrumenterFactory {
1817

18+
@SuppressWarnings("deprecation") // to support old semconv
1919
public static Instrumenter<ClickHouseDbRequest, Void> createInstrumenter(
2020
String instrumenterName, Function<Throwable, String> errorCodeExtractor) {
2121
ClickHouseAttributesGetter dbAttributesGetter =
@@ -24,10 +24,11 @@ public static Instrumenter<ClickHouseDbRequest, Void> createInstrumenter(
2424
return Instrumenter.<ClickHouseDbRequest, Void>builder(
2525
GlobalOpenTelemetry.get(),
2626
instrumenterName,
27-
DbClientSpanNameExtractor.create(dbAttributesGetter))
28-
.addAttributesExtractor(DbClientAttributesExtractor.create(dbAttributesGetter))
27+
DbClientSpanNameExtractor.createForMigration(dbAttributesGetter))
2928
.addAttributesExtractor(
30-
ServerAttributesExtractor.create(new ClickHouseNetworkAttributesGetter()))
29+
SqlClientAttributesExtractor.<ClickHouseDbRequest, Void>builder(dbAttributesGetter)
30+
.setTableAttribute(null)
31+
.build())
3132
.addOperationMetrics(DbClientMetrics.get())
3233
.buildInstrumenter(SpanKindExtractor.alwaysClient());
3334
}

instrumentation/clickhouse/clickhouse-client-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/clickhouse/common/ClickHouseNetworkAttributesGetter.java

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)