Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
32aeb83
Added db.operation.parameter span attributes
AlixBa Apr 14, 2025
d1ebd7e
add backward method on SqlStatementInfo for callers without parameters
AlixBa Apr 16, 2025
2d48543
fix SlickTest with span attributes
AlixBa Apr 16, 2025
26d635c
Merge branch 'open-telemetry:main' into add-operation-parameter-attri…
AlixBa Apr 17, 2025
b3365f1
Replace db.operation.parameter by db.query.parameter
AlixBa Apr 17, 2025
a785cd6
Change -D flag name; explicit Advices override
AlixBa Apr 28, 2025
235f221
Merge branch 'open-telemetry:main' into add-operation-parameter-attri…
AlixBa Apr 28, 2025
99ce35f
format with spotlessApply
AlixBa Apr 28, 2025
bf67533
fix tests not updated with latest changes
AlixBa Apr 28, 2025
9d6f08a
fix naming conventions & tests database initialization
AlixBa Apr 28, 2025
f6702f0
only store parameters if feature enabled & factorize agent advices & …
AlixBa Apr 30, 2025
07f8c5f
Merge branch 'main' into add-operation-parameter-attributes
AlixBa Apr 30, 2025
18eb6f8
send back emptyMap instead of null
AlixBa Apr 30, 2025
b22ffe6
update metadata.yaml with capture-query-parameters
AlixBa May 4, 2025
5b872e6
Merge branch 'main' into add-operation-parameter-attributes
AlixBa May 4, 2025
c4c877e
review
laurit May 9, 2025
949dbb7
Merge branch 'main' into add-operation-parameter-attributes
laurit May 9, 2025
afa52b8
simplify and fix test
laurit May 9, 2025
bd16245
simplify
laurit May 9, 2025
ceff3f5
merge
laurit May 14, 2025
ff936a4
update metadata
laurit May 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public final class CommonConfig {
private final Set<String> knownHttpRequestMethods;
private final EnduserConfig enduserConfig;
private final boolean statementSanitizationEnabled;
private final boolean queryParameterEnabled;
private final boolean emitExperimentalHttpClientTelemetry;
private final boolean emitExperimentalHttpServerTelemetry;
private final boolean redactQueryParameters;
Expand Down Expand Up @@ -56,6 +57,9 @@ public CommonConfig(InstrumentationConfig config) {
new ArrayList<>(HttpConstants.KNOWN_METHODS)));
statementSanitizationEnabled =
config.getBoolean("otel.instrumentation.common.db-statement-sanitizer.enabled", true);
// TODO change with common conf key
queryParameterEnabled =
config.getBoolean("otel.instrumentation.jdbc.query-parameter.enabled", false);
emitExperimentalHttpClientTelemetry =
config.getBoolean("otel.instrumentation.http.client.emit-experimental-telemetry", false);
redactQueryParameters =
Expand Down Expand Up @@ -107,6 +111,10 @@ public boolean isStatementSanitizationEnabled() {
return statementSanitizationEnabled;
}

public boolean isQueryParameterEnabled() {
return queryParameterEnabled;
}

public boolean shouldEmitExperimentalHttpClientTelemetry() {
return emitExperimentalHttpClientTelemetry;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.semconv.AttributeKeyTemplate;
import java.util.Collection;
import java.util.Map;

/**
* Extractor of <a
Expand All @@ -38,6 +40,8 @@ public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
AttributeKey.stringKey("db.collection.name");
private static final AttributeKey<Long> DB_OPERATION_BATCH_SIZE =
AttributeKey.longKey("db.operation.batch.size");
private static final AttributeKeyTemplate<String> DB_QUERY_PARAMETER =
AttributeKeyTemplate.stringKeyTemplate("db.query.parameter");

/** Creates the SQL client attributes extractor with default configuration. */
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
Expand All @@ -58,26 +62,34 @@ public static <REQUEST, RESPONSE> SqlClientAttributesExtractorBuilder<REQUEST, R

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

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

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

Collection<String> rawQueryTexts = getter.getRawQueryTexts(request);
Map<Integer, Object> preparedStatementParameters = getter.getQueryParameters(request);

if (rawQueryTexts.isEmpty()) {
return;
}

Long batchSize = getter.getBatchSize(request);
boolean isBatch = batchSize != null && batchSize > 1;

if (SemconvStability.emitOldDatabaseSemconv()) {
if (rawQueryTexts.size() == 1) { // for backcompat(?)
String rawQueryText = rawQueryTexts.iterator().next();
Expand All @@ -91,12 +103,11 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST
if (!SQL_CALL.equals(operation)) {
internalSet(attributes, oldSemconvTableAttribute, sanitizedStatement.getMainIdentifier());
}
setQueryParameters(attributes, sanitizedStatement, isBatch, preparedStatementParameters);
}
}

if (SemconvStability.emitStableDatabaseSemconv()) {
Long batchSize = getter.getBatchSize(request);
boolean isBatch = batchSize != null && batchSize > 1;
if (isBatch) {
internalSet(attributes, DB_OPERATION_BATCH_SIZE, batchSize);
}
Expand All @@ -112,6 +123,7 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST
if (!SQL_CALL.equals(operation)) {
internalSet(attributes, DB_COLLECTION_NAME, sanitizedStatement.getMainIdentifier());
}
setQueryParameters(attributes, sanitizedStatement, isBatch, preparedStatementParameters);
} else {
MultiQuery multiQuery =
MultiQuery.analyze(getter.getRawQueryTexts(request), statementSanitizationEnabled);
Expand All @@ -129,6 +141,33 @@ public void onStart(AttributesBuilder attributes, Context parentContext, REQUEST
}
}

private void setQueryParameters(
AttributesBuilder attributes,
SqlStatementInfo sanitizedStatement,
boolean isBatch,
Map<Integer, Object> preparedStatementParameters) {
if (sanitizedStatement.getParameters() != null && queryParameterEnabled && !isBatch) {
int currentPreparedStatementParametersIndex = 1;
for (Map.Entry<String, String> entry : sanitizedStatement.getParameters().entrySet()) {
// in this case it means that the sanitizer parsed an existing ?
// or a postgres marked parameter. So we'll replace with data from the REQUEST
String key = entry.getKey();
String value = entry.getValue();
if (preparedStatementParameters != null
&& (value.equalsIgnoreCase("?") || value.startsWith("$"))
&& preparedStatementParameters.containsKey(currentPreparedStatementParametersIndex)) {
internalSet(
attributes,
DB_QUERY_PARAMETER.getAttributeKey(key),
stringifyParameter(
preparedStatementParameters.get(currentPreparedStatementParametersIndex++)));
} else {
internalSet(attributes, DB_QUERY_PARAMETER.getAttributeKey(key), value);
}
}
}
}

// String.join is not available on android
private static String join(String delimiter, Collection<String> collection) {
StringBuilder builder = new StringBuilder();
Expand All @@ -140,4 +179,18 @@ private static String join(String delimiter, Collection<String> collection) {
}
return builder.toString();
}

// TODO define all string repr of objects
public static String stringifyParameter(Object object) {
if (object == null) {
return "<null>";
} else if (object instanceof String) {
return String.format("'%s'", object);
} else if (object instanceof Number) {
Number number = (Number) object;
return String.format("%s", number);
}

return String.format("<%s>", object.getClass().getSimpleName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
final SqlClientAttributesGetter<REQUEST, RESPONSE> getter;
AttributeKey<String> oldSemconvTableAttribute = DB_SQL_TABLE;
boolean statementSanitizationEnabled = true;
boolean queryParameterEnabled = false;

SqlClientAttributesExtractorBuilder(SqlClientAttributesGetter<REQUEST, RESPONSE> getter) {
this.getter = getter;
Expand Down Expand Up @@ -48,12 +49,25 @@ public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setStatementSaniti
return this;
}

/**
* Sets whether the {@code db.query.parameter.<key>} attributes extracted by the constructed
* {@link SqlClientAttributesExtractor} should be opted-in. If set to {@code true}, all parameters
* masked by the sanitization and all parameters from {@code PreparedStatement} will be exposed as
* attributes. Disabled by default.
*/
@CanIgnoreReturnValue
public SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> setQueryParameterEnabled(
boolean queryParameterEnabled) {
this.queryParameterEnabled = queryParameterEnabled;
return this;
}

/**
* Returns a new {@link SqlClientAttributesExtractor} with the settings of this {@link
* SqlClientAttributesExtractorBuilder}.
*/
public AttributesExtractor<REQUEST, RESPONSE> build() {
return new SqlClientAttributesExtractor<>(
getter, oldSemconvTableAttribute, statementSanitizationEnabled);
getter, oldSemconvTableAttribute, statementSanitizationEnabled, queryParameterEnabled);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static java.util.Collections.singleton;

import java.util.Collection;
import java.util.Map;
import javax.annotation.Nullable;

/**
Expand Down Expand Up @@ -66,4 +67,11 @@ default Collection<String> getRawQueryTexts(REQUEST request) {
default Long getBatchSize(REQUEST request) {
return null;
}

/** TODO */
// TODO: make this required to implement
@Nullable
default Map<Integer, Object> getQueryParameters(REQUEST request) {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,23 @@
package io.opentelemetry.instrumentation.api.incubator.semconv.db;

import com.google.auto.value.AutoValue;
import java.util.Map;
import javax.annotation.Nullable;

@AutoValue
public abstract class SqlStatementInfo {

public static SqlStatementInfo create(
@Nullable String fullStatement,
@Nullable String operation,
@Nullable String identifier,
@Nullable Map<String, String> parameters) {
return new AutoValue_SqlStatementInfo(fullStatement, operation, identifier, parameters);
}

public static SqlStatementInfo create(
@Nullable String fullStatement, @Nullable String operation, @Nullable String identifier) {
return new AutoValue_SqlStatementInfo(fullStatement, operation, identifier);
return new AutoValue_SqlStatementInfo(fullStatement, operation, identifier, null);
}

@Nullable
Expand All @@ -24,4 +33,7 @@ public static SqlStatementInfo create(

@Nullable
public abstract String getMainIdentifier();

@Nullable
public abstract Map<String, String> getParameters();
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public SqlStatementInfo sanitize(@Nullable String statement) {

public SqlStatementInfo sanitize(@Nullable String statement, SqlDialect dialect) {
if (!statementSanitizationEnabled || statement == null) {
return SqlStatementInfo.create(statement, null, null);
return SqlStatementInfo.create(statement, null, null, null);
}
// sanitization result will not be cached for statements larger than the threshold to avoid
// cache growing too large
Expand Down
Loading
Loading