Skip to content

Commit

Permalink
Implement some of the stable database semantic conventions (#11575)
Browse files Browse the repository at this point in the history
Co-authored-by: Trask Stalnaker <[email protected]>
  • Loading branch information
heyams and trask authored Oct 29, 2024
1 parent 0521ac1 commit 61c99cf
Show file tree
Hide file tree
Showing 69 changed files with 982 additions and 315 deletions.
13 changes: 13 additions & 0 deletions instrumentation-api-incubator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,17 @@ tasks {
sourcesJar {
dependsOn("generateJflex")
}

val testStableSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database")
}

val testBothSemconv by registering(Test::class) {
jvmArgs("-Dotel.semconv-stability.opt-in=database/dup")
}

check {
dependsOn(testStableSemconv)
dependsOn(testBothSemconv)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;

/**
* Extractor of <a
Expand All @@ -26,7 +27,11 @@ public final class DbClientAttributesExtractor<REQUEST, RESPONSE>

// copied from DbIncubatingAttributes
private static final AttributeKey<String> DB_STATEMENT = AttributeKey.stringKey("db.statement");
private static final AttributeKey<String> DB_QUERY_TEXT = AttributeKey.stringKey("db.query.text");

private static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation");
private static final AttributeKey<String> DB_OPERATION_NAME =
AttributeKey.stringKey("db.operation.name");

/** Creates the database client attributes extractor with default configuration. */
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
Expand All @@ -42,7 +47,13 @@ public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
super.onStart(attributes, parentContext, request);

internalSet(attributes, DB_STATEMENT, getter.getStatement(request));
internalSet(attributes, DB_OPERATION, getter.getOperation(request));
if (SemconvStability.emitStableDatabaseSemconv()) {
internalSet(attributes, DB_QUERY_TEXT, getter.getDbQueryText(request));
internalSet(attributes, DB_OPERATION_NAME, getter.getDbOperationName(request));
}
if (SemconvStability.emitOldDatabaseSemconv()) {
internalSet(attributes, DB_STATEMENT, getter.getDbQueryText(request));
internalSet(attributes, DB_OPERATION, getter.getDbOperationName(request));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,33 @@
*/
public interface DbClientAttributesGetter<REQUEST> extends DbClientCommonAttributesGetter<REQUEST> {

/**
* @deprecated Use {@link #getDbQueryText(REQUEST)} instead.
*/
@Deprecated
@Nullable
String getStatement(REQUEST request);
default String getStatement(REQUEST request) {
return null;
}

// TODO: make this required to implement
@Nullable
String getOperation(REQUEST request);
default String getDbQueryText(REQUEST request) {
return getStatement(request);
}

/**
* @deprecated Use {@link #getDbOperationName(REQUEST)} instead.
*/
@Deprecated
@Nullable
default String getOperation(REQUEST request) {
return null;
}

// TODO: make this required to implement
@Nullable
default String getDbOperationName(REQUEST request) {
return getOperation(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.api.internal.SpanKey;
import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider;
import javax.annotation.Nullable;
Expand All @@ -21,6 +22,7 @@ abstract class DbClientCommonAttributesExtractor<

// copied from DbIncubatingAttributes
private static final AttributeKey<String> DB_NAME = AttributeKey.stringKey("db.name");
private static final AttributeKey<String> DB_NAMESPACE = AttributeKey.stringKey("db.namespace");
private static final AttributeKey<String> DB_SYSTEM = AttributeKey.stringKey("db.system");
private static final AttributeKey<String> DB_USER = AttributeKey.stringKey("db.user");
private static final AttributeKey<String> DB_CONNECTION_STRING =
Expand All @@ -32,12 +34,18 @@ abstract class DbClientCommonAttributesExtractor<
this.getter = getter;
}

@SuppressWarnings("deprecation") // until old db semconv are dropped
@Override
public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
internalSet(attributes, DB_SYSTEM, getter.getSystem(request));
internalSet(attributes, DB_USER, getter.getUser(request));
internalSet(attributes, DB_NAME, getter.getName(request));
internalSet(attributes, DB_CONNECTION_STRING, getter.getConnectionString(request));
internalSet(attributes, DB_SYSTEM, getter.getDbSystem(request));
if (SemconvStability.emitStableDatabaseSemconv()) {
internalSet(attributes, DB_NAMESPACE, getter.getDbNamespace(request));
}
if (SemconvStability.emitOldDatabaseSemconv()) {
internalSet(attributes, DB_USER, getter.getUser(request));
internalSet(attributes, DB_NAME, getter.getDbNamespace(request));
internalSet(attributes, DB_CONNECTION_STRING, getter.getConnectionString(request));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,38 @@
/** An interface for getting attributes common to database clients. */
public interface DbClientCommonAttributesGetter<REQUEST> {

@Deprecated
@Nullable
String getSystem(REQUEST request);
default String getSystem(REQUEST request) {
return null;
}

// TODO: make this required to implement
@Nullable
default String getDbSystem(REQUEST request) {
return getSystem(request);
}

@Deprecated
@Nullable
String getUser(REQUEST request);

/**
* @deprecated Use {@link #getDbNamespace(Object)} instead.
*/
@Deprecated
@Nullable
default String getName(REQUEST request) {
return null;
}

// TODO: make this required to implement
@Nullable
String getName(REQUEST request);
default String getDbNamespace(REQUEST request) {
return getName(request);
}

@Deprecated
@Nullable
String getConnectionString(REQUEST request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ public abstract class DbClientSpanNameExtractor<REQUEST> implements SpanNameExtr
* Returns a {@link SpanNameExtractor} that constructs the span name according to DB semantic
* conventions: {@code <db.operation> <db.name>}.
*
* @see DbClientAttributesGetter#getOperation(Object) used to extract {@code <db.operation>}.
* @see DbClientAttributesGetter#getName(Object) used to extract {@code <db.name>}.
* @see DbClientAttributesGetter#getDbOperationName(Object) used to extract {@code
* <db.operation.name>}.
* @see DbClientAttributesGetter#getDbNamespace(Object) used to extract {@code <db.namespace>}.
*/
public static <REQUEST> SpanNameExtractor<REQUEST> create(
DbClientAttributesGetter<REQUEST> getter) {
Expand All @@ -26,7 +27,7 @@ public static <REQUEST> SpanNameExtractor<REQUEST> create(
* conventions: {@code <db.operation> <db.name>.<identifier>}.
*
* @see SqlStatementInfo#getOperation() used to extract {@code <db.operation>}.
* @see DbClientAttributesGetter#getName(Object) used to extract {@code <db.name>}.
* @see DbClientAttributesGetter#getDbNamespace(Object) used to extract {@code <db.namespace>}.
* @see SqlStatementInfo#getMainIdentifier() used to extract {@code <db.table>} or stored
* procedure name.
*/
Expand Down Expand Up @@ -72,9 +73,9 @@ private GenericDbClientSpanNameExtractor(DbClientAttributesGetter<REQUEST> gette

@Override
public String extract(REQUEST request) {
String dbName = getter.getName(request);
String operation = getter.getOperation(request);
return computeSpanName(dbName, operation, null);
String namespace = getter.getDbNamespace(request);
String operationName = getter.getDbOperationName(request);
return computeSpanName(namespace, operationName, null);
}
}

Expand All @@ -92,10 +93,10 @@ private SqlClientSpanNameExtractor(SqlClientAttributesGetter<REQUEST> getter) {

@Override
public String extract(REQUEST request) {
String dbName = getter.getName(request);
SqlStatementInfo sanitizedStatement = sanitizer.sanitize(getter.getRawStatement(request));
String namespace = getter.getDbNamespace(request);
SqlStatementInfo sanitizedStatement = sanitizer.sanitize(getter.getRawQueryText(request));
return computeSpanName(
dbName, sanitizedStatement.getOperation(), sanitizedStatement.getMainIdentifier());
namespace, sanitizedStatement.getOperation(), sanitizedStatement.getMainIdentifier());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;

/**
* Extractor of <a
Expand All @@ -19,7 +20,7 @@
*
* <p>It sets the same set of attributes as {@link DbClientAttributesExtractor} plus an additional
* <code>db.sql.table</code> attribute. The raw SQL statements returned by the {@link
* SqlClientAttributesGetter#getRawStatement(Object)} method are sanitized before use, all statement
* SqlClientAttributesGetter#getRawQueryText(Object)} method are sanitized before use, all statement
* parameters are removed.
*/
public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
Expand All @@ -28,7 +29,12 @@ public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>

// copied from DbIncubatingAttributes
private static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation");
private static final AttributeKey<String> DB_OPERATION_NAME =
AttributeKey.stringKey("db.operation.name");
private static final AttributeKey<String> DB_STATEMENT = AttributeKey.stringKey("db.statement");
private static final AttributeKey<String> DB_QUERY_TEXT = AttributeKey.stringKey("db.query.text");
private static final AttributeKey<String> DB_COLLECTION_NAME =
AttributeKey.stringKey("db.collection.name");

/** Creates the SQL client attributes extractor with default configuration. */
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
Expand All @@ -49,32 +55,46 @@ public static <REQUEST, RESPONSE> SqlClientAttributesExtractorBuilder<REQUEST, R
// sanitizer is also used to extract operation and table name, so we have it always enable here
private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(true);

private final AttributeKey<String> dbTableAttribute;
private final AttributeKey<String> oldSemconvTableAttribute;
private final boolean statementSanitizationEnabled;

SqlClientAttributesExtractor(
SqlClientAttributesGetter<REQUEST> getter,
AttributeKey<String> dbTableAttribute,
AttributeKey<String> oldSemconvTableAttribute,
boolean statementSanitizationEnabled) {
super(getter);
this.dbTableAttribute = dbTableAttribute;
this.oldSemconvTableAttribute = oldSemconvTableAttribute;
this.statementSanitizationEnabled = statementSanitizationEnabled;
}

@Override
public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST request) {
super.onStart(attributes, parentContext, request);

String rawStatement = getter.getRawStatement(request);
SqlStatementInfo sanitizedStatement = sanitizer.sanitize(rawStatement);
String rawQueryText = getter.getRawQueryText(request);
SqlStatementInfo sanitizedStatement = sanitizer.sanitize(rawQueryText);
String operation = sanitizedStatement.getOperation();
internalSet(
attributes,
DB_STATEMENT,
statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawStatement);
internalSet(attributes, DB_OPERATION, operation);
if (SemconvStability.emitStableDatabaseSemconv()) {
internalSet(
attributes,
DB_QUERY_TEXT,
statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText);
internalSet(attributes, DB_OPERATION_NAME, operation);
}
if (SemconvStability.emitOldDatabaseSemconv()) {
internalSet(
attributes,
DB_STATEMENT,
statementSanitizationEnabled ? sanitizedStatement.getFullStatement() : rawQueryText);
internalSet(attributes, DB_OPERATION, operation);
}
if (!SQL_CALL.equals(operation)) {
internalSet(attributes, dbTableAttribute, sanitizedStatement.getMainIdentifier());
if (SemconvStability.emitStableDatabaseSemconv()) {
internalSet(attributes, DB_COLLECTION_NAME, sanitizedStatement.getMainIdentifier());
}
if (SemconvStability.emitOldDatabaseSemconv()) {
internalSet(attributes, oldSemconvTableAttribute, sanitizedStatement.getMainIdentifier());
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,21 @@ public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
private static final AttributeKey<String> DB_SQL_TABLE = AttributeKey.stringKey("db.sql.table");

final SqlClientAttributesGetter<REQUEST> getter;
AttributeKey<String> dbTableAttribute = DB_SQL_TABLE;
AttributeKey<String> oldSemconvTableAttribute = DB_SQL_TABLE;
boolean statementSanitizationEnabled = true;

SqlClientAttributesExtractorBuilder(SqlClientAttributesGetter<REQUEST> getter) {
this.getter = getter;
}

/**
* Configures the extractor to set the table value extracted by the {@link
* SqlClientAttributesExtractor} under the {@code dbTableAttribute} key. By default, the <code>
* db.sql.table</code> attribute is used.
*
* @param dbTableAttribute The {@link AttributeKey} under which the table extracted by the {@link
* SqlClientAttributesExtractor} will be stored.
* @deprecated not needed anymore since the new semantic conventions always use db.collection.name
*/
@CanIgnoreReturnValue
@Deprecated
public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setTableAttribute(
AttributeKey<String> dbTableAttribute) {
this.dbTableAttribute = requireNonNull(dbTableAttribute);
AttributeKey<String> oldSemconvTableAttribute) {
this.oldSemconvTableAttribute = requireNonNull(oldSemconvTableAttribute);
return this;
}

Expand All @@ -58,6 +54,6 @@ public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setStatementSaniti
*/
public AttributesExtractor<REQUEST, RESPONSE> build() {
return new SqlClientAttributesExtractor<>(
getter, dbTableAttribute, statementSanitizationEnabled);
getter, oldSemconvTableAttribute, statementSanitizationEnabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,18 @@ public interface SqlClientAttributesGetter<REQUEST>
/**
* Get the raw SQL statement. The value returned by this method is later sanitized by the {@link
* SqlClientAttributesExtractor} before being set as span attribute.
*
* @deprecated Use {@link #getRawQueryText(Object)} instead.
*/
@Deprecated
@Nullable
String getRawStatement(REQUEST request);
default String getRawStatement(REQUEST request) {
return null;
}

// TODO: make this required to implement
@Nullable
default String getRawQueryText(REQUEST request) {
return getRawStatement(request);
}
}
Loading

0 comments on commit 61c99cf

Please sign in to comment.