Skip to content

Commit 91368ad

Browse files
committed
Add DB client collection semconv API
1 parent f3bc077 commit 91368ad

17 files changed

Lines changed: 373 additions & 134 deletions

File tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldDatabaseSemconv;
99
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv;
10+
import static io.opentelemetry.semconv.DbAttributes.DB_COLLECTION_NAME;
1011
import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE;
1112
import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_BATCH_SIZE;
1213
import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME;
@@ -99,6 +100,7 @@ static <REQUEST, RESPONSE> void onStartCommon(
99100
attributes.put(
100101
DB_SYSTEM_NAME, SemconvStability.stableDbSystemName(getter.getDbSystemName(request)));
101102
attributes.put(DB_NAMESPACE, getter.getDbNamespace(request));
103+
attributes.put(DB_COLLECTION_NAME, getter.getDbCollectionName(request));
102104
attributes.put(DB_QUERY_TEXT, getter.getDbQueryText(request));
103105
attributes.put(DB_OPERATION_NAME, getter.getDbOperationName(request));
104106
attributes.put(DB_QUERY_SUMMARY, getter.getDbQuerySummary(request));
@@ -112,7 +114,7 @@ static <REQUEST, RESPONSE> void onStartCommon(
112114
attributes.put(DB_NAME, getter.getDbName(request));
113115
attributes.put(DB_CONNECTION_STRING, getter.getConnectionString(request));
114116
attributes.put(DB_STATEMENT, getter.getDbQueryText(request));
115-
attributes.put(DB_OPERATION, getter.getDbOperationName(request));
117+
attributes.put(DB_OPERATION, getter.getDbOperation(request));
116118
}
117119

118120
// Query parameters are an incubating feature and work with both old and new semconv

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,17 @@ default String getDbQuerySummary(REQUEST request) {
3838
@Nullable
3939
String getDbOperationName(REQUEST request);
4040

41+
/**
42+
* Returns the old db.operation value. This is only used for old semantic conventions.
43+
*
44+
* @deprecated Use {@link #getDbOperationName(Object)} instead.
45+
*/
46+
@Deprecated // to be removed in 3.0
47+
@Nullable
48+
default String getDbOperation(REQUEST request) {
49+
return getDbOperationName(request);
50+
}
51+
4152
// TODO: make this required to implement
4253
String getDbSystemName(REQUEST request);
4354

@@ -59,6 +70,12 @@ default String getDbSystem(REQUEST request) {
5970
@Nullable
6071
String getDbNamespace(REQUEST request);
6172

73+
// TODO: make this required to implement?
74+
@Nullable
75+
default String getDbCollectionName(REQUEST request) {
76+
return null;
77+
}
78+
6279
/**
6380
* Returns the old db.name value. This is only used for old semantic conventions.
6481
*

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

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,15 @@ public String extract(REQUEST request) {
163163
if (querySummary != null) {
164164
return querySummary;
165165
}
166-
String operationName = getter.getDbOperationName(request);
167-
return computeSpanNameStable(getter, request, operationName, null, null);
166+
return computeSpanNameStable(
167+
getter,
168+
request,
169+
getter.getDbOperationName(request),
170+
getter.getDbCollectionName(request),
171+
null);
168172
}
169173
String dbName = getter.getDbName(request);
170-
String operationName = getter.getDbOperationName(request);
174+
String operationName = getter.getDbOperation(request);
171175
return computeSpanName(dbName, operationName, null, null);
172176
}
173177
}
@@ -189,16 +193,10 @@ public String extract(REQUEST request) {
189193

190194
if (rawQueryTexts.isEmpty()) {
191195
if (emitStableDatabaseSemconv()) {
192-
String querySummary = getter.getDbQuerySummary(request);
193-
if (querySummary != null) {
194-
return querySummary;
195-
}
196-
String operationName = getter.getDbOperationName(request);
197-
return computeSpanNameStable(getter, request, operationName, null, null);
196+
return computeSpanNameStable(getter, request, null, null, null);
198197
}
199198
String dbName = getter.getDbName(request);
200-
String operationName = getter.getDbOperationName(request);
201-
return computeSpanName(dbName, operationName, null, null);
199+
return computeSpanName(dbName, null, null, null);
202200
}
203201

204202
if (!emitStableDatabaseSemconv()) {
@@ -224,7 +222,11 @@ public String extract(REQUEST request) {
224222
return batch ? "BATCH " + querySummary : querySummary;
225223
}
226224
return computeSpanNameStable(
227-
getter, request, batch ? "BATCH" : null, null, analyzedQuery.getStoredProcedureName());
225+
getter,
226+
request,
227+
batch ? "BATCH" : null,
228+
analyzedQuery.getCollectionName(),
229+
analyzedQuery.getStoredProcedureName());
228230
}
229231

230232
MultiQuery multiQuery = MultiQuery.analyzeWithSummary(rawQueryTexts, dialect);
@@ -276,9 +278,6 @@ public String extract(REQUEST request) {
276278
SqlQuery analyzedQuery = SqlQueryAnalyzerUtil.analyze(rawQuery, dialect);
277279
operationName = analyzedQuery.getOperationName();
278280
}
279-
if (operationName == null) {
280-
operationName = getter.getDbOperationName(request);
281-
}
282281
return computeSpanName(dbName, operationName, null, null);
283282
}
284283
}

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

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77

88
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldDatabaseSemconv;
99
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv;
10+
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.stableDbSystemName;
1011
import static io.opentelemetry.semconv.DbAttributes.DB_COLLECTION_NAME;
12+
import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE;
1113
import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_BATCH_SIZE;
1214
import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME;
1315
import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY;
1416
import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_TEXT;
1517
import static io.opentelemetry.semconv.DbAttributes.DB_STORED_PROCEDURE_NAME;
18+
import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME;
1619

1720
import io.opentelemetry.api.common.AttributeKey;
1821
import io.opentelemetry.api.common.AttributesBuilder;
@@ -22,7 +25,9 @@
2225
import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider;
2326
import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesExtractor;
2427
import io.opentelemetry.instrumentation.api.semconv.network.internal.InternalNetworkAttributesExtractor;
28+
import io.opentelemetry.semconv.AttributeKeyTemplate;
2529
import java.util.Collection;
30+
import java.util.Map;
2631
import javax.annotation.Nullable;
2732

2833
/**
@@ -39,8 +44,15 @@ public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
3944
implements AttributesExtractor<REQUEST, RESPONSE>, SpanKeyProvider {
4045

4146
// copied from DbIncubatingAttributes
47+
private static final AttributeKey<String> DB_NAME = AttributeKey.stringKey("db.name");
48+
private static final AttributeKey<String> DB_SYSTEM = AttributeKey.stringKey("db.system");
49+
private static final AttributeKey<String> DB_USER = AttributeKey.stringKey("db.user");
50+
private static final AttributeKey<String> DB_CONNECTION_STRING =
51+
AttributeKey.stringKey("db.connection_string");
4252
private static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation");
4353
private static final AttributeKey<String> DB_STATEMENT = AttributeKey.stringKey("db.statement");
54+
private static final AttributeKeyTemplate<String> DB_QUERY_PARAMETER =
55+
AttributeKeyTemplate.stringKeyTemplate("db.query.parameter");
4456

4557
/** Creates the SQL client attributes extractor with default configuration. */
4658
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
@@ -141,10 +153,24 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST
141153
}
142154
}
143155

144-
// calling this last so explicit getDbOperationName(), getDbCollectionName(),
145-
// getDbQueryText(), and getDbQuerySummary() implementations can override
146-
// the parsed values from above
147-
DbClientAttributesExtractor.onStartCommon(attributes, getter, request, captureQueryParameters);
156+
if (emitStableDatabaseSemconv()) {
157+
attributes.put(DB_SYSTEM_NAME, stableDbSystemName(getter.getDbSystemName(request)));
158+
attributes.put(DB_NAMESPACE, getter.getDbNamespace(request));
159+
}
160+
if (emitOldDatabaseSemconv()) {
161+
attributes.put(DB_SYSTEM, getter.getDbSystem(request));
162+
attributes.put(DB_USER, getter.getUser(request));
163+
attributes.put(DB_NAME, getter.getDbName(request));
164+
attributes.put(DB_CONNECTION_STRING, getter.getConnectionString(request));
165+
}
166+
if (captureQueryParameters && !isBatch) {
167+
Map<String, String> queryParameters = getter.getDbQueryParameters(request);
168+
if (queryParameters != null && !queryParameters.isEmpty()) {
169+
for (Map.Entry<String, String> entry : queryParameters.entrySet()) {
170+
attributes.put(DB_QUERY_PARAMETER.getAttributeKey(entry.getKey()), entry.getValue());
171+
}
172+
}
173+
}
148174
serverAttributesExtractor.onStart(attributes, parentContext, request);
149175
}
150176

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,12 @@ public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setCaptureQueryPar
6868
}
6969

7070
/**
71-
* Sets whether the instrumentation knows that its SQL-like language or request shape is limited
72-
* to a single operation and collection.
71+
* Sets whether {@code db.operation.name} and {@code db.collection.name} can be derived from
72+
* {@code db.query.text}.
7373
*
74-
* <p>For most instrumentations, enabling this will produce invalid semantic conventions.
74+
* <p>Enable this only when the database system does not support query text with multiple
75+
* operations or multiple collections in non-batch operations. For most instrumentations, enabling
76+
* this will produce invalid semantic conventions.
7577
*/
7678
@CanIgnoreReturnValue
7779
public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setSingleOperationAndCollection(

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

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

88
import java.util.Collection;
9-
import javax.annotation.Nullable;
109

1110
/**
1211
* An interface for getting SQL database client attributes.
@@ -23,33 +22,65 @@ public interface SqlClientAttributesGetter<REQUEST, RESPONSE>
2322
extends DbClientAttributesGetter<REQUEST, RESPONSE> {
2423

2524
/**
26-
* SqlClientAttributesExtractor will try to populate db.operation.name based on {@link
27-
* #getRawQueryTexts(REQUEST)}, but this can be used to explicitly provide the operation name.
25+
* SQL instrumentations must not override or call this method.
26+
*
27+
* <p>Provide raw query text through {@link #getRawQueryTexts(REQUEST)} instead. When the database
28+
* system does not support query text with multiple operations in non-batch operations, enable
29+
* {@link SqlClientAttributesExtractorBuilder#setSingleOperationAndCollection(boolean)} and {@link
30+
* SqlClientAttributesExtractor} will derive {@code db.operation.name} from {@code db.query.text}.
31+
*
32+
* @throws UnsupportedOperationException always
2833
*/
2934
@Override
30-
@Nullable
3135
default String getDbOperationName(REQUEST request) {
32-
return null;
36+
throw new UnsupportedOperationException(
37+
"SQL instrumentations derive db.operation.name from the raw query text");
3338
}
3439

3540
/**
36-
* SqlClientAttributesExtractor will try to populate db.query.text based on {@link
37-
* #getRawQueryTexts(REQUEST)}, but this can be used to explicitly provide the query text.
41+
* SQL instrumentations must not override or call this method.
42+
*
43+
* <p>Provide raw query text through {@link #getRawQueryTexts(REQUEST)} instead. {@link
44+
* SqlClientAttributesExtractor} will derive {@code db.query.text} from the raw query text.
45+
*
46+
* @throws UnsupportedOperationException always
3847
*/
3948
@Override
40-
@Nullable
4149
default String getDbQueryText(REQUEST request) {
42-
return null;
50+
throw new UnsupportedOperationException(
51+
"SQL instrumentations derive db.query.text from the raw query text");
4352
}
4453

4554
/**
46-
* SqlClientAttributesExtractor will try to populate db.query.summary based on {@link
47-
* #getRawQueryTexts(REQUEST)}, but this can be used to explicitly provide the query summary.
55+
* SQL instrumentations must not override or call this method.
56+
*
57+
* <p>Provide raw query text through {@link #getRawQueryTexts(REQUEST)} instead. {@link
58+
* SqlClientAttributesExtractor} will derive {@code db.query.summary} from the raw query text.
59+
*
60+
* @throws UnsupportedOperationException always
4861
*/
4962
@Override
50-
@Nullable
5163
default String getDbQuerySummary(REQUEST request) {
52-
return null;
64+
throw new UnsupportedOperationException(
65+
"SQL instrumentations derive db.query.summary from the raw query text");
66+
}
67+
68+
/**
69+
* SQL instrumentations must not override or call this method.
70+
*
71+
* <p>Provide raw query text through {@link #getRawQueryTexts(REQUEST)} instead. When the database
72+
* system does not support query text with multiple collections in non-batch operations, enable
73+
* {@link SqlClientAttributesExtractorBuilder#setSingleOperationAndCollection(boolean)} and {@link
74+
* SqlClientAttributesExtractor} will derive {@code db.collection.name} from {@code
75+
* db.query.text}. Do not enable that option when the database system supports query text with
76+
* multiple collections in non-batch operations.
77+
*
78+
* @throws UnsupportedOperationException always
79+
*/
80+
@Override
81+
default String getDbCollectionName(REQUEST request) {
82+
throw new UnsupportedOperationException(
83+
"SQL instrumentations derive db.collection.name from the raw query text");
5384
}
5485

5586
/** Returns the SQL dialect used by the database. */

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitOldDatabaseSemconv;
99
import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emitStableDatabaseSemconv;
1010
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
11+
import static io.opentelemetry.semconv.DbAttributes.DB_COLLECTION_NAME;
1112
import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE;
1213
import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME;
1314
import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_SUMMARY;
@@ -50,6 +51,11 @@ public String getDbNamespace(Map<String, String> map) {
5051
return map.get("db.namespace");
5152
}
5253

54+
@Override
55+
public String getDbCollectionName(Map<String, String> map) {
56+
return map.get("db.collection.name");
57+
}
58+
5359
@Deprecated
5460
@Override
5561
public String getConnectionString(Map<String, String> map) {
@@ -71,6 +77,12 @@ public String getDbQuerySummary(Map<String, String> map) {
7177
public String getDbOperationName(Map<String, String> map) {
7278
return map.get("db.operation.name");
7379
}
80+
81+
@Deprecated
82+
@Override
83+
public String getDbOperation(Map<String, String> map) {
84+
return map.get("db.operation");
85+
}
7486
}
7587

7688
@SuppressWarnings("deprecation") // TODO DB_CONNECTION_STRING deprecation
@@ -81,9 +93,11 @@ void shouldExtractAllAvailableAttributes() {
8193
request.put("db.system", "myDb");
8294
request.put("db.user", "username");
8395
request.put("db.namespace", "potatoes");
96+
request.put("db.collection.name", "potato");
8497
request.put("db.connection_string", "mydb:///potatoes");
8598
request.put("db.query.text", "SELECT * FROM potato");
8699
request.put("db.query_summary", "SELECT potato");
100+
request.put("db.operation", "old SELECT");
87101
request.put("db.operation.name", "SELECT");
88102

89103
Context context = Context.root();
@@ -108,7 +122,8 @@ void shouldExtractAllAvailableAttributes() {
108122
entry(DB_NAME, "potatoes"),
109123
entry(DB_CONNECTION_STRING, "mydb:///potatoes"),
110124
entry(DB_STATEMENT, "SELECT * FROM potato"),
111-
entry(DB_OPERATION, "SELECT"),
125+
entry(DB_OPERATION, "old SELECT"),
126+
entry(DB_COLLECTION_NAME, "potato"),
112127
entry(DB_NAMESPACE, "potatoes"),
113128
entry(DB_QUERY_TEXT, "SELECT * FROM potato"),
114129
entry(DB_QUERY_SUMMARY, "SELECT potato"),
@@ -121,11 +136,12 @@ void shouldExtractAllAvailableAttributes() {
121136
entry(DB_NAME, "potatoes"),
122137
entry(DB_CONNECTION_STRING, "mydb:///potatoes"),
123138
entry(DB_STATEMENT, "SELECT * FROM potato"),
124-
entry(DB_OPERATION, "SELECT"));
139+
entry(DB_OPERATION, "old SELECT"));
125140
} else if (emitStableDatabaseSemconv()) {
126141
assertThat(startAttributes.build())
127142
.containsOnly(
128143
entry(DB_SYSTEM_NAME, "myDb"),
144+
entry(DB_COLLECTION_NAME, "potato"),
129145
entry(DB_NAMESPACE, "potatoes"),
130146
entry(DB_QUERY_TEXT, "SELECT * FROM potato"),
131147
entry(DB_QUERY_SUMMARY, "SELECT potato"),

0 commit comments

Comments
 (0)