Db client error type for JDBC (#13331)

Co-authored-by: Jay DeLuca <jaydeluca4@gmail.com>
This commit is contained in:
Gregor Zeitlinger 2025-03-31 14:40:16 +02:00 committed by GitHub
parent 67f99cb33c
commit e2cce4cc16
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 378 additions and 108 deletions

View File

@ -23,23 +23,23 @@ import io.opentelemetry.instrumentation.api.internal.SemconvStability;
*/ */
public final class DbClientAttributesExtractor<REQUEST, RESPONSE> public final class DbClientAttributesExtractor<REQUEST, RESPONSE>
extends DbClientCommonAttributesExtractor< extends DbClientCommonAttributesExtractor<
REQUEST, RESPONSE, DbClientAttributesGetter<REQUEST>> { REQUEST, RESPONSE, DbClientAttributesGetter<REQUEST, RESPONSE>> {
// copied from DbIncubatingAttributes // copied from DbIncubatingAttributes
private static final AttributeKey<String> DB_STATEMENT = AttributeKey.stringKey("db.statement"); 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_QUERY_TEXT = AttributeKey.stringKey("db.query.text");
static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation"); static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation");
static final AttributeKey<String> DB_OPERATION_NAME = AttributeKey.stringKey("db.operation.name"); static final AttributeKey<String> DB_OPERATION_NAME = AttributeKey.stringKey("db.operation.name");
static final AttributeKey<Long> DB_RESPONSE_STATUS_CODE = static final AttributeKey<String> DB_RESPONSE_STATUS_CODE =
AttributeKey.longKey("db.response.status_code"); AttributeKey.stringKey("db.response.status_code");
/** Creates the database client attributes extractor with default configuration. */ /** Creates the database client attributes extractor with default configuration. */
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create( public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
DbClientAttributesGetter<REQUEST> getter) { DbClientAttributesGetter<REQUEST, RESPONSE> getter) {
return new DbClientAttributesExtractor<>(getter); return new DbClientAttributesExtractor<>(getter);
} }
DbClientAttributesExtractor(DbClientAttributesGetter<REQUEST> getter) { DbClientAttributesExtractor(DbClientAttributesGetter<REQUEST, RESPONSE> getter) {
super(getter); super(getter);
} }

View File

@ -18,7 +18,8 @@ import javax.annotation.Nullable;
* from the attribute methods, but implement as many as possible for best compliance with the * from the attribute methods, but implement as many as possible for best compliance with the
* OpenTelemetry specification. * OpenTelemetry specification.
*/ */
public interface DbClientAttributesGetter<REQUEST> extends DbClientCommonAttributesGetter<REQUEST> { public interface DbClientAttributesGetter<REQUEST, RESPONSE>
extends DbClientCommonAttributesGetter<REQUEST, RESPONSE> {
/** /**
* @deprecated Use {@link #getDbQueryText(REQUEST)} instead. * @deprecated Use {@link #getDbQueryText(REQUEST)} instead.

View File

@ -6,6 +6,7 @@
package io.opentelemetry.instrumentation.api.incubator.semconv.db; package io.opentelemetry.instrumentation.api.incubator.semconv.db;
import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.AttributesBuilder; import io.opentelemetry.api.common.AttributesBuilder;
@ -17,7 +18,7 @@ import io.opentelemetry.instrumentation.api.internal.SpanKeyProvider;
import javax.annotation.Nullable; import javax.annotation.Nullable;
abstract class DbClientCommonAttributesExtractor< abstract class DbClientCommonAttributesExtractor<
REQUEST, RESPONSE, GETTER extends DbClientCommonAttributesGetter<REQUEST>> REQUEST, RESPONSE, GETTER extends DbClientCommonAttributesGetter<REQUEST, RESPONSE>>
implements AttributesExtractor<REQUEST, RESPONSE>, SpanKeyProvider { implements AttributesExtractor<REQUEST, RESPONSE>, SpanKeyProvider {
// copied from DbIncubatingAttributes // copied from DbIncubatingAttributes
@ -29,6 +30,8 @@ abstract class DbClientCommonAttributesExtractor<
private static final AttributeKey<String> DB_USER = AttributeKey.stringKey("db.user"); private static final AttributeKey<String> DB_USER = AttributeKey.stringKey("db.user");
private static final AttributeKey<String> DB_CONNECTION_STRING = private static final AttributeKey<String> DB_CONNECTION_STRING =
AttributeKey.stringKey("db.connection_string"); AttributeKey.stringKey("db.connection_string");
private static final AttributeKey<String> DB_RESPONSE_STATUS_CODE =
AttributeKey.stringKey("db.response.status_code");
final GETTER getter; final GETTER getter;
@ -60,7 +63,16 @@ abstract class DbClientCommonAttributesExtractor<
Context context, Context context,
REQUEST request, REQUEST request,
@Nullable RESPONSE response, @Nullable RESPONSE response,
@Nullable Throwable error) {} @Nullable Throwable error) {
if (SemconvStability.emitStableDatabaseSemconv()) {
if (error != null) {
internalSet(attributes, ERROR_TYPE, error.getClass().getName());
}
if (error != null || response != null) {
internalSet(attributes, DB_RESPONSE_STATUS_CODE, getter.getResponseStatus(response, error));
}
}
}
/** /**
* This method is internal and is hence not for public use. Its API is unstable and can change at * This method is internal and is hence not for public use. Its API is unstable and can change at

View File

@ -8,7 +8,7 @@ package io.opentelemetry.instrumentation.api.incubator.semconv.db;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** An interface for getting attributes common to database clients. */ /** An interface for getting attributes common to database clients. */
public interface DbClientCommonAttributesGetter<REQUEST> { public interface DbClientCommonAttributesGetter<REQUEST, RESPONSE> {
@Deprecated @Deprecated
@Nullable @Nullable
@ -44,4 +44,9 @@ public interface DbClientCommonAttributesGetter<REQUEST> {
@Deprecated @Deprecated
@Nullable @Nullable
String getConnectionString(REQUEST request); String getConnectionString(REQUEST request);
@Nullable
default String getResponseStatus(@Nullable RESPONSE response, @Nullable Throwable error) {
return null;
}
} }

View File

@ -32,11 +32,7 @@ final class DbClientMetricsAdvice {
SqlClientAttributesExtractor.DB_COLLECTION_NAME, SqlClientAttributesExtractor.DB_COLLECTION_NAME,
DbClientCommonAttributesExtractor.DB_NAMESPACE, DbClientCommonAttributesExtractor.DB_NAMESPACE,
DbClientAttributesExtractor.DB_OPERATION_NAME, DbClientAttributesExtractor.DB_OPERATION_NAME,
// will be implemented in
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/12804
DbClientAttributesExtractor.DB_RESPONSE_STATUS_CODE, DbClientAttributesExtractor.DB_RESPONSE_STATUS_CODE,
// will be implemented in
// https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/12804
ErrorAttributes.ERROR_TYPE, ErrorAttributes.ERROR_TYPE,
NetworkAttributes.NETWORK_PEER_ADDRESS, NetworkAttributes.NETWORK_PEER_ADDRESS,
NetworkAttributes.NETWORK_PEER_PORT, NetworkAttributes.NETWORK_PEER_PORT,

View File

@ -20,7 +20,7 @@ public abstract class DbClientSpanNameExtractor<REQUEST> implements SpanNameExtr
* @see DbClientAttributesGetter#getDbNamespace(Object) used to extract {@code <db.namespace>}. * @see DbClientAttributesGetter#getDbNamespace(Object) used to extract {@code <db.namespace>}.
*/ */
public static <REQUEST> SpanNameExtractor<REQUEST> create( public static <REQUEST> SpanNameExtractor<REQUEST> create(
DbClientAttributesGetter<REQUEST> getter) { DbClientAttributesGetter<REQUEST, ?> getter) {
return new GenericDbClientSpanNameExtractor<>(getter); return new GenericDbClientSpanNameExtractor<>(getter);
} }
@ -34,7 +34,7 @@ public abstract class DbClientSpanNameExtractor<REQUEST> implements SpanNameExtr
* procedure name. * procedure name.
*/ */
public static <REQUEST> SpanNameExtractor<REQUEST> create( public static <REQUEST> SpanNameExtractor<REQUEST> create(
SqlClientAttributesGetter<REQUEST> getter) { SqlClientAttributesGetter<REQUEST, ?> getter) {
return new SqlClientSpanNameExtractor<>(getter); return new SqlClientSpanNameExtractor<>(getter);
} }
@ -67,9 +67,9 @@ public abstract class DbClientSpanNameExtractor<REQUEST> implements SpanNameExtr
private static final class GenericDbClientSpanNameExtractor<REQUEST> private static final class GenericDbClientSpanNameExtractor<REQUEST>
extends DbClientSpanNameExtractor<REQUEST> { extends DbClientSpanNameExtractor<REQUEST> {
private final DbClientAttributesGetter<REQUEST> getter; private final DbClientAttributesGetter<REQUEST, ?> getter;
private GenericDbClientSpanNameExtractor(DbClientAttributesGetter<REQUEST> getter) { private GenericDbClientSpanNameExtractor(DbClientAttributesGetter<REQUEST, ?> getter) {
this.getter = getter; this.getter = getter;
} }
@ -87,9 +87,9 @@ public abstract class DbClientSpanNameExtractor<REQUEST> implements SpanNameExtr
// a dedicated sanitizer just for extracting the operation and identifier name // a dedicated sanitizer just for extracting the operation and identifier name
private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(true); private static final SqlStatementSanitizer sanitizer = SqlStatementSanitizer.create(true);
private final SqlClientAttributesGetter<REQUEST> getter; private final SqlClientAttributesGetter<REQUEST, ?> getter;
private SqlClientSpanNameExtractor(SqlClientAttributesGetter<REQUEST> getter) { private SqlClientSpanNameExtractor(SqlClientAttributesGetter<REQUEST, ?> getter) {
this.getter = getter; this.getter = getter;
} }

View File

@ -26,7 +26,7 @@ import java.util.Collection;
*/ */
public final class SqlClientAttributesExtractor<REQUEST, RESPONSE> public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
extends DbClientCommonAttributesExtractor< extends DbClientCommonAttributesExtractor<
REQUEST, RESPONSE, SqlClientAttributesGetter<REQUEST>> { REQUEST, RESPONSE, SqlClientAttributesGetter<REQUEST, RESPONSE>> {
// copied from DbIncubatingAttributes // copied from DbIncubatingAttributes
private static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation"); private static final AttributeKey<String> DB_OPERATION = AttributeKey.stringKey("db.operation");
@ -41,7 +41,7 @@ public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
/** Creates the SQL client attributes extractor with default configuration. */ /** Creates the SQL client attributes extractor with default configuration. */
public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create( public static <REQUEST, RESPONSE> AttributesExtractor<REQUEST, RESPONSE> create(
SqlClientAttributesGetter<REQUEST> getter) { SqlClientAttributesGetter<REQUEST, RESPONSE> getter) {
return SqlClientAttributesExtractor.<REQUEST, RESPONSE>builder(getter).build(); return SqlClientAttributesExtractor.<REQUEST, RESPONSE>builder(getter).build();
} }
@ -50,7 +50,7 @@ public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
* client attributes extractor. * client attributes extractor.
*/ */
public static <REQUEST, RESPONSE> SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> builder( public static <REQUEST, RESPONSE> SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> builder(
SqlClientAttributesGetter<REQUEST> getter) { SqlClientAttributesGetter<REQUEST, RESPONSE> getter) {
return new SqlClientAttributesExtractorBuilder<>(getter); return new SqlClientAttributesExtractorBuilder<>(getter);
} }
@ -62,7 +62,7 @@ public final class SqlClientAttributesExtractor<REQUEST, RESPONSE>
private final boolean statementSanitizationEnabled; private final boolean statementSanitizationEnabled;
SqlClientAttributesExtractor( SqlClientAttributesExtractor(
SqlClientAttributesGetter<REQUEST> getter, SqlClientAttributesGetter<REQUEST, RESPONSE> getter,
AttributeKey<String> oldSemconvTableAttribute, AttributeKey<String> oldSemconvTableAttribute,
boolean statementSanitizationEnabled) { boolean statementSanitizationEnabled) {
super(getter); super(getter);

View File

@ -17,11 +17,11 @@ public final class SqlClientAttributesExtractorBuilder<REQUEST, RESPONSE> {
// copied from DbIncubatingAttributes // copied from DbIncubatingAttributes
private static final AttributeKey<String> DB_SQL_TABLE = AttributeKey.stringKey("db.sql.table"); private static final AttributeKey<String> DB_SQL_TABLE = AttributeKey.stringKey("db.sql.table");
final SqlClientAttributesGetter<REQUEST> getter; final SqlClientAttributesGetter<REQUEST, RESPONSE> getter;
AttributeKey<String> oldSemconvTableAttribute = DB_SQL_TABLE; AttributeKey<String> oldSemconvTableAttribute = DB_SQL_TABLE;
boolean statementSanitizationEnabled = true; boolean statementSanitizationEnabled = true;
SqlClientAttributesExtractorBuilder(SqlClientAttributesGetter<REQUEST> getter) { SqlClientAttributesExtractorBuilder(SqlClientAttributesGetter<REQUEST, RESPONSE> getter) {
this.getter = getter; this.getter = getter;
} }

View File

@ -22,8 +22,8 @@ import javax.annotation.Nullable;
* from the attribute methods, but implement as many as possible for best compliance with the * from the attribute methods, but implement as many as possible for best compliance with the
* OpenTelemetry specification. * OpenTelemetry specification.
*/ */
public interface SqlClientAttributesGetter<REQUEST> public interface SqlClientAttributesGetter<REQUEST, RESPONSE>
extends DbClientCommonAttributesGetter<REQUEST> { extends DbClientCommonAttributesGetter<REQUEST, RESPONSE> {
/** /**
* Get the raw SQL statement. The value returned by this method is later sanitized by the {@link * Get the raw SQL statement. The value returned by this method is later sanitized by the {@link

View File

@ -21,7 +21,8 @@ import org.junit.jupiter.api.Test;
class DbClientAttributesExtractorTest { class DbClientAttributesExtractorTest {
static final class TestAttributesGetter implements DbClientAttributesGetter<Map<String, String>> { static final class TestAttributesGetter
implements DbClientAttributesGetter<Map<String, String>, Void> {
@Override @Override
public String getDbSystem(Map<String, String> map) { public String getDbSystem(Map<String, String> map) {
return map.get("db.system"); return map.get("db.system");

View File

@ -52,7 +52,7 @@ class DbClientMetricsTest {
Attributes responseAttributes = Attributes responseAttributes =
Attributes.builder() Attributes.builder()
.put(DbClientAttributesExtractor.DB_RESPONSE_STATUS_CODE, 200) .put(DbClientAttributesExtractor.DB_RESPONSE_STATUS_CODE, "200")
.put(ErrorAttributes.ERROR_TYPE, "400") .put(ErrorAttributes.ERROR_TYPE, "400")
.put(NetworkAttributes.NETWORK_PEER_ADDRESS, "1.2.3.4") .put(NetworkAttributes.NETWORK_PEER_ADDRESS, "1.2.3.4")
.put(NetworkAttributes.NETWORK_PEER_PORT, 8080) .put(NetworkAttributes.NETWORK_PEER_PORT, 8080)
@ -104,7 +104,7 @@ class DbClientMetricsTest {
equalTo(ServerAttributes.SERVER_PORT, 1234), equalTo(ServerAttributes.SERVER_PORT, 1234),
equalTo( equalTo(
DbClientAttributesExtractor.DB_RESPONSE_STATUS_CODE, DbClientAttributesExtractor.DB_RESPONSE_STATUS_CODE,
200), "200"),
equalTo(ErrorAttributes.ERROR_TYPE, "400"), equalTo(ErrorAttributes.ERROR_TYPE, "400"),
equalTo( equalTo(
NetworkAttributes.NETWORK_PEER_ADDRESS, "1.2.3.4"), NetworkAttributes.NETWORK_PEER_ADDRESS, "1.2.3.4"),

View File

@ -19,8 +19,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
class DbClientSpanNameExtractorTest { class DbClientSpanNameExtractorTest {
@Mock DbClientAttributesGetter<DbRequest> dbAttributesGetter; @Mock DbClientAttributesGetter<DbRequest, Void> dbAttributesGetter;
@Mock SqlClientAttributesGetter<DbRequest> sqlAttributesGetter; @Mock SqlClientAttributesGetter<DbRequest, Void> sqlAttributesGetter;
@Test @Test
void shouldExtractFullSpanName() { void shouldExtractFullSpanName() {

View File

@ -26,7 +26,8 @@ import org.junit.jupiter.api.Test;
@SuppressWarnings("deprecation") // using deprecated semconv @SuppressWarnings("deprecation") // using deprecated semconv
class SqlClientAttributesExtractorTest { class SqlClientAttributesExtractorTest {
static class TestAttributesGetter implements SqlClientAttributesGetter<Map<String, Object>> { static class TestAttributesGetter
implements SqlClientAttributesGetter<Map<String, Object>, Void> {
@Override @Override
public Collection<String> getRawQueryTexts(Map<String, Object> map) { public Collection<String> getRawQueryTexts(Map<String, Object> map) {
@ -71,7 +72,7 @@ class SqlClientAttributesExtractorTest {
} }
static class TestMultiAttributesGetter extends TestAttributesGetter static class TestMultiAttributesGetter extends TestAttributesGetter
implements SqlClientAttributesGetter<Map<String, Object>> { implements SqlClientAttributesGetter<Map<String, Object>, Void> {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override

View File

@ -7,12 +7,14 @@ package io.opentelemetry.javaagent.instrumentation.cassandra.v3_0;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import com.datastax.driver.core.ExecutionInfo;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class CassandraSqlAttributesGetter implements SqlClientAttributesGetter<CassandraRequest> { final class CassandraSqlAttributesGetter
implements SqlClientAttributesGetter<CassandraRequest, ExecutionInfo> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -8,12 +8,14 @@ package io.opentelemetry.javaagent.instrumentation.cassandra.v4_0;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import com.datastax.oss.driver.api.core.CqlIdentifier; import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class CassandraSqlAttributesGetter implements SqlClientAttributesGetter<CassandraRequest> { final class CassandraSqlAttributesGetter
implements SqlClientAttributesGetter<CassandraRequest, ExecutionInfo> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -8,11 +8,13 @@ package io.opentelemetry.instrumentation.cassandra.v4_4;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import com.datastax.oss.driver.api.core.CqlIdentifier; import com.datastax.oss.driver.api.core.CqlIdentifier;
import com.datastax.oss.driver.api.core.cql.ExecutionInfo;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class CassandraSqlAttributesGetter implements SqlClientAttributesGetter<CassandraRequest> { final class CassandraSqlAttributesGetter
implements SqlClientAttributesGetter<CassandraRequest, ExecutionInfo> {
// copied from DbIncubatingAttributes.DbSystemIncubatingValues // copied from DbIncubatingAttributes.DbSystemIncubatingValues
private static final String CASSANDRA = "cassandra"; private static final String CASSANDRA = "cassandra";

View File

@ -5,11 +5,13 @@
package io.opentelemetry.javaagent.instrumentation.clickhouse; package io.opentelemetry.javaagent.instrumentation.clickhouse;
import com.clickhouse.client.ClickHouseException;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class ClickHouseAttributesGetter implements DbClientAttributesGetter<ClickHouseDbRequest> { final class ClickHouseAttributesGetter
implements DbClientAttributesGetter<ClickHouseDbRequest, Void> {
@Nullable @Nullable
@Override @Override
@ -58,4 +60,13 @@ final class ClickHouseAttributesGetter implements DbClientAttributesGetter<Click
public String getConnectionString(ClickHouseDbRequest request) { public String getConnectionString(ClickHouseDbRequest request) {
return null; return null;
} }
@Nullable
@Override
public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) {
if (error instanceof ClickHouseException) {
return Integer.toString(((ClickHouseException) error).getErrorCode());
}
return null;
}
} }

View File

@ -7,10 +7,12 @@ package io.opentelemetry.javaagent.instrumentation.clickhouse;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_RESPONSE_STATUS_CODE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@ -28,12 +30,14 @@ import com.clickhouse.data.ClickHouseFormat;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterAll;
@ -196,6 +200,12 @@ class ClickHouseClientTest {
assertThat(thrown).isInstanceOf(ClickHouseException.class); assertThat(thrown).isInstanceOf(ClickHouseException.class);
List<AttributeAssertion> assertions =
new ArrayList<>(attributeAssertions("select * from non_existent_table", "SELECT"));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(DB_RESPONSE_STATUS_CODE, "60"));
assertions.add(equalTo(ERROR_TYPE, "com.clickhouse.client.ClickHouseException"));
}
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
trace.hasSpansSatisfyingExactly( trace.hasSpansSatisfyingExactly(
@ -204,8 +214,7 @@ class ClickHouseClientTest {
.hasKind(SpanKind.CLIENT) .hasKind(SpanKind.CLIENT)
.hasStatus(StatusData.error()) .hasStatus(StatusData.error())
.hasException(thrown) .hasException(thrown)
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(assertions)));
attributeAssertions("select * from non_existent_table", "SELECT"))));
} }
@Test @Test

View File

@ -9,7 +9,8 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttribu
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class CouchbaseAttributesGetter implements DbClientAttributesGetter<CouchbaseRequestInfo> { final class CouchbaseAttributesGetter
implements DbClientAttributesGetter<CouchbaseRequestInfo, Void> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -16,13 +16,14 @@ import java.util.logging.Logger;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.elasticsearch.client.Response;
/** /**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at * This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time. * any time.
*/ */
final class ElasticsearchDbAttributesGetter final class ElasticsearchDbAttributesGetter
implements DbClientAttributesGetter<ElasticsearchRestRequest> { implements DbClientAttributesGetter<ElasticsearchRestRequest, Response> {
private static final Logger logger = private static final Logger logger =
Logger.getLogger(ElasticsearchDbAttributesGetter.class.getName()); Logger.getLogger(ElasticsearchDbAttributesGetter.class.getName());
@ -91,4 +92,14 @@ final class ElasticsearchDbAttributesGetter
ElasticsearchEndpointDefinition endpointDefinition = request.getEndpointDefinition(); ElasticsearchEndpointDefinition endpointDefinition = request.getEndpointDefinition();
return endpointDefinition != null ? endpointDefinition.getEndpointName() : null; return endpointDefinition != null ? endpointDefinition.getEndpointName() : null;
} }
@Nullable
@Override
public String getResponseStatus(@Nullable Response response, @Nullable Throwable error) {
if (response != null) {
int httpStatus = response.getStatusLine().getStatusCode();
return httpStatus >= 400 && httpStatus < 600 ? Integer.toString(httpStatus) : null;
}
return null;
}
} }

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.common.AttributeKey.longKey;
import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -17,14 +18,17 @@ import static org.awaitility.Awaitility.await;
import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING; import static org.elasticsearch.cluster.ClusterName.CLUSTER_NAME_SETTING;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.io.File; import java.io.File;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -162,6 +166,21 @@ class Elasticsearch53SpringTemplateTest {
assertThatThrownBy(() -> template.refresh(indexName)) assertThatThrownBy(() -> template.refresh(indexName))
.isInstanceOf(IndexNotFoundException.class); .isInstanceOf(IndexNotFoundException.class);
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), "RefreshAction"),
equalTo(stringKey("elasticsearch.request"), "RefreshRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName)));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "org.elasticsearch.index.IndexNotFoundException"));
}
IndexNotFoundException expectedException = new IndexNotFoundException("no such index"); IndexNotFoundException expectedException = new IndexNotFoundException("no such index");
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
@ -172,14 +191,7 @@ class Elasticsearch53SpringTemplateTest {
.hasNoParent() .hasNoParent()
.hasStatus(StatusData.error()) .hasStatus(StatusData.error())
.hasException(expectedException) .hasException(expectedException)
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(assertions)));
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "RefreshAction"),
equalTo(stringKey("elasticsearch.action"), "RefreshAction"),
equalTo(stringKey("elasticsearch.request"), "RefreshRequest"),
equalTo(stringKey("elasticsearch.request.indices"), indexName))));
} }
@Test @Test

View File

@ -8,9 +8,10 @@ package io.opentelemetry.javaagent.instrumentation.elasticsearch.transport;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.elasticsearch.action.ActionResponse;
final class ElasticsearchTransportAttributesGetter final class ElasticsearchTransportAttributesGetter
implements DbClientAttributesGetter<ElasticTransportRequest> { implements DbClientAttributesGetter<ElasticTransportRequest, ActionResponse> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.common.AttributeKey.longKey;
import static io.opentelemetry.api.common.AttributeKey.stringKey; import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -16,6 +17,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Named.named; import static org.junit.jupiter.api.Named.named;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier; import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
@ -103,6 +105,21 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
.isInstanceOf(IndexNotFoundException.class) .isInstanceOf(IndexNotFoundException.class)
.hasMessage(expectedException.getMessage()); .hasMessage(expectedException.getMessage());
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, "invalid-index")));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "org.elasticsearch.index.IndexNotFoundException"));
}
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
trace.hasSpansSatisfyingExactly( trace.hasSpansSatisfyingExactly(
@ -118,14 +135,7 @@ public abstract class AbstractElasticsearchNodeClientTest extends AbstractElasti
.hasParent(trace.getSpan(0)) .hasParent(trace.getSpan(0))
.hasStatus(StatusData.error()) .hasStatus(StatusData.error())
.hasException(expectedException) .hasException(expectedException)
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(assertions),
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, "invalid-index")),
span -> span ->
span.hasName("callback") span.hasName("callback")
.hasKind(SpanKind.INTERNAL) .hasKind(SpanKind.INTERNAL)

View File

@ -11,6 +11,7 @@ import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStability
import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName; import static io.opentelemetry.instrumentation.testing.util.TelemetryDataUtil.orderByRootSpanName;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS;
import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_PORT;
@ -22,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Named.named; import static org.junit.jupiter.api.Named.named;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier; import io.opentelemetry.instrumentation.testing.util.ThrowingSupplier;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
@ -118,6 +120,21 @@ public abstract class AbstractElasticsearchTransportClientTest
.isInstanceOf(IndexNotFoundException.class) .isInstanceOf(IndexNotFoundException.class)
.hasMessage(expectedException.getMessage()); .hasMessage(expectedException.getMessage());
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, "invalid-index")));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "org.elasticsearch.transport.RemoteTransportException"));
}
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
trace.hasSpansSatisfyingExactly( trace.hasSpansSatisfyingExactly(
@ -140,14 +157,7 @@ public abstract class AbstractElasticsearchTransportClientTest
equalTo( equalTo(
EXCEPTION_TYPE, EXCEPTION_TYPE,
RemoteTransportException.class.getName()))) RemoteTransportException.class.getName())))
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(assertions),
equalTo(
maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.ELASTICSEARCH),
equalTo(maybeStable(DB_OPERATION), "GetAction"),
equalTo(ELASTICSEARCH_ACTION, "GetAction"),
equalTo(ELASTICSEARCH_REQUEST, "GetRequest"),
equalTo(ELASTICSEARCH_REQUEST_INDICES, "invalid-index")),
span -> span ->
span.hasName("callback") span.hasName("callback")
.hasKind(SpanKind.INTERNAL) .hasKind(SpanKind.INTERNAL)

View File

@ -11,7 +11,7 @@ import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class GeodeDbAttributesGetter implements DbClientAttributesGetter<GeodeRequest> { final class GeodeDbAttributesGetter implements DbClientAttributesGetter<GeodeRequest, Void> {
private static final SqlStatementSanitizer sanitizer = private static final SqlStatementSanitizer sanitizer =
SqlStatementSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled()); SqlStatementSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled());

View File

@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.influxdb.v2_4;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class InfluxDbAttributesGetter implements DbClientAttributesGetter<InfluxDbRequest> { final class InfluxDbAttributesGetter implements DbClientAttributesGetter<InfluxDbRequest, Void> {
@Nullable @Nullable
@Override @Override

View File

@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.jdbc.internal;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter;
import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo; import io.opentelemetry.instrumentation.jdbc.internal.dbinfo.DbInfo;
import java.sql.SQLException;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -14,7 +15,7 @@ import javax.annotation.Nullable;
* This class is internal and is hence not for public use. Its APIs are unstable and can change at * This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time. * any time.
*/ */
public final class JdbcAttributesGetter implements SqlClientAttributesGetter<DbRequest> { public final class JdbcAttributesGetter implements SqlClientAttributesGetter<DbRequest, Void> {
@Nullable @Nullable
@Override @Override
@ -52,4 +53,13 @@ public final class JdbcAttributesGetter implements SqlClientAttributesGetter<DbR
public Long getBatchSize(DbRequest request) { public Long getBatchSize(DbRequest request) {
return request.getBatchSize(); return request.getBatchSize();
} }
@Nullable
@Override
public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) {
if (error instanceof SQLException) {
return Integer.toString(((SQLException) error).getErrorCode());
}
return null;
}
} }

View File

@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.jdbc.datasource;
import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric; import static io.opentelemetry.instrumentation.testing.junit.db.DbClientMetricsTestUtil.assertDurationMetric;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME;
@ -15,10 +16,17 @@ import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_BATCH_SIZE; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_BATCH_SIZE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION_NAME;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_QUERY_TEXT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_RESPONSE_STATUS_CODE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SQL_TABLE; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SQL_TABLE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SYSTEM_NAME;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import io.opentelemetry.instrumentation.api.internal.SemconvStability; import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection; import io.opentelemetry.instrumentation.jdbc.internal.OpenTelemetryConnection;
@ -32,6 +40,7 @@ import java.sql.Statement;
import javax.sql.DataSource; import javax.sql.DataSource;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import org.mockito.Mockito;
@SuppressWarnings("deprecation") // using deprecated semconv @SuppressWarnings("deprecation") // using deprecated semconv
class JdbcTelemetryTest { class JdbcTelemetryTest {
@ -66,6 +75,55 @@ class JdbcTelemetryTest {
SERVER_PORT); SERVER_PORT);
} }
@Test
void error() throws SQLException {
assumeTrue(SemconvStability.emitStableDatabaseSemconv());
JdbcTelemetry telemetry = JdbcTelemetry.builder(testing.getOpenTelemetry()).build();
DataSource source = spy(new TestDataSource());
Connection connection = spy(source.getConnection());
Statement statement = spy(connection.createStatement());
when(source.getConnection()).thenReturn(connection);
when(connection.createStatement()).thenReturn(statement);
doThrow(new SQLException("BOOM", "state", 42)).when(statement).execute(Mockito.anyString());
DataSource dataSource = telemetry.wrap(source);
assertThatCode(
() ->
testing.runWithSpan(
"parent",
() -> dataSource.getConnection().createStatement().execute("SELECT 1;")))
.isInstanceOf(SQLException.class);
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactly(
span -> span.hasName("parent"),
span -> span.hasName("TestDataSource.getConnection"),
span ->
span.hasName("SELECT dbname")
.hasAttributesSatisfyingExactly(
equalTo(DB_SYSTEM_NAME, "postgresql"),
equalTo(DB_OPERATION_NAME, "SELECT"),
equalTo(DB_NAMESPACE, "dbname"),
equalTo(DB_QUERY_TEXT, "SELECT ?;"),
equalTo(DB_RESPONSE_STATUS_CODE, "42"),
equalTo(SERVER_ADDRESS, "127.0.0.1"),
equalTo(SERVER_PORT, 5432),
equalTo(ERROR_TYPE, "java.sql.SQLException"))));
assertDurationMetric(
testing,
"io.opentelemetry.jdbc",
DB_NAMESPACE,
DB_OPERATION_NAME,
DB_RESPONSE_STATUS_CODE,
DB_SYSTEM_NAME,
ERROR_TYPE,
SERVER_ADDRESS,
SERVER_PORT);
}
@Test @Test
void buildWithAllInstrumentersDisabled() throws SQLException { void buildWithAllInstrumentersDisabled() throws SQLException {
JdbcTelemetry telemetry = JdbcTelemetry telemetry =

View File

@ -11,7 +11,7 @@ import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisRequest> { final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisRequest, Void> {
private static final RedisCommandSanitizer sanitizer = private static final RedisCommandSanitizer sanitizer =
RedisCommandSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled()); RedisCommandSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled());

View File

@ -9,7 +9,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttribu
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisRequest> { final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisRequest, Void> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -9,7 +9,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttribu
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisRequest> { final class JedisDbAttributesGetter implements DbClientAttributesGetter<JedisRequest, Void> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -10,7 +10,8 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttribu
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class LettuceDbAttributesGetter implements DbClientAttributesGetter<RedisCommand<?, ?, ?>> { final class LettuceDbAttributesGetter
implements DbClientAttributesGetter<RedisCommand<?, ?, ?>, Void> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -8,6 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.lettuce.v4_0;
import static io.opentelemetry.api.common.AttributeKey.booleanKey; import static io.opentelemetry.api.common.AttributeKey.booleanKey;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
@ -30,11 +31,16 @@ import com.lambdaworks.redis.codec.Utf8StringCodec;
import com.lambdaworks.redis.protocol.AsyncCommand; import com.lambdaworks.redis.protocol.AsyncCommand;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.test.utils.PortUtils;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -412,6 +418,15 @@ class LettuceAsyncClientTest {
assertThat(completedExceptionally).isTrue(); assertThat(completedExceptionally).isTrue();
}); });
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
equalTo(maybeStable(DB_SYSTEM), "redis"),
equalTo(maybeStable(DB_OPERATION), "DEL")));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "java.lang.IllegalStateException"));
}
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
trace.hasSpansSatisfyingExactly( trace.hasSpansSatisfyingExactly(
@ -420,9 +435,7 @@ class LettuceAsyncClientTest {
.hasKind(SpanKind.CLIENT) .hasKind(SpanKind.CLIENT)
.hasStatus(StatusData.error()) .hasStatus(StatusData.error())
.hasException(new IllegalStateException("TestException")) .hasException(new IllegalStateException("TestException"))
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(assertions)));
equalTo(maybeStable(DB_SYSTEM), "redis"),
equalTo(maybeStable(DB_OPERATION), "DEL"))));
} }
@Test @Test

View File

@ -15,7 +15,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class LettuceDbAttributesGetter implements DbClientAttributesGetter<RedisCommand<?, ?, ?>> { final class LettuceDbAttributesGetter
implements DbClientAttributesGetter<RedisCommand<?, ?, ?>, Void> {
private static final RedisCommandSanitizer sanitizer = private static final RedisCommandSanitizer sanitizer =
RedisCommandSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled()); RedisCommandSanitizer.create(AgentCommonConfig.get().isStatementSanitizationEnabled());

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.common.AttributeKey.booleanKey;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS; import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
@ -32,10 +33,15 @@ import io.lettuce.core.protocol.AsyncCommand;
import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.test.utils.PortUtils; import io.opentelemetry.instrumentation.test.utils.PortUtils;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@ -412,6 +418,17 @@ class LettuceAsyncClientTest extends AbstractLettuceClientTest {
assertThat(completedExceptionally).isTrue(); assertThat(completedExceptionally).isTrue();
}); });
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
equalTo(maybeStable(DB_SYSTEM), "redis"),
equalTo(maybeStable(DB_STATEMENT), "DEL key1 key2"),
equalTo(maybeStable(DB_OPERATION), "DEL")));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(ERROR_TYPE, "java.lang.IllegalStateException"));
}
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
trace.hasSpansSatisfyingExactly( trace.hasSpansSatisfyingExactly(
@ -420,10 +437,7 @@ class LettuceAsyncClientTest extends AbstractLettuceClientTest {
.hasKind(SpanKind.CLIENT) .hasKind(SpanKind.CLIENT)
.hasStatus(StatusData.error()) .hasStatus(StatusData.error())
.hasException(new IllegalStateException("TestException")) .hasException(new IllegalStateException("TestException"))
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(assertions)));
equalTo(maybeStable(DB_SYSTEM), "redis"),
equalTo(maybeStable(DB_STATEMENT), "DEL key1 key2"),
equalTo(maybeStable(DB_OPERATION), "DEL"))));
} }
@Test @Test

View File

@ -5,6 +5,7 @@
package io.opentelemetry.instrumentation.mongo.v3_1; package io.opentelemetry.instrumentation.mongo.v3_1;
import com.mongodb.MongoException;
import com.mongodb.ServerAddress; import com.mongodb.ServerAddress;
import com.mongodb.connection.ConnectionDescription; import com.mongodb.connection.ConnectionDescription;
import com.mongodb.event.CommandStartedEvent; import com.mongodb.event.CommandStartedEvent;
@ -23,7 +24,7 @@ import org.bson.codecs.EncoderContext;
import org.bson.json.JsonWriter; import org.bson.json.JsonWriter;
import org.bson.json.JsonWriterSettings; import org.bson.json.JsonWriterSettings;
class MongoDbAttributesGetter implements DbClientAttributesGetter<CommandStartedEvent> { class MongoDbAttributesGetter implements DbClientAttributesGetter<CommandStartedEvent, Void> {
// copied from DbIncubatingAttributes.DbSystemIncubatingValues // copied from DbIncubatingAttributes.DbSystemIncubatingValues
private static final String MONGODB = "mongodb"; private static final String MONGODB = "mongodb";
@ -97,6 +98,15 @@ class MongoDbAttributesGetter implements DbClientAttributesGetter<CommandStarted
return event.getCommandName(); return event.getCommandName();
} }
@Nullable
@Override
public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) {
if (error instanceof MongoException) {
return Integer.toString(((MongoException) error).getCode());
}
return null;
}
String sanitizeStatement(BsonDocument command) { String sanitizeStatement(BsonDocument command) {
StringBuilderWriter stringWriter = new StringBuilderWriter(128); StringBuilderWriter stringWriter = new StringBuilderWriter(128);
// jsonWriterSettings is generally not null but could be due to security manager or unknown // jsonWriterSettings is generally not null but could be due to security manager or unknown

View File

@ -8,9 +8,10 @@ package io.opentelemetry.javaagent.instrumentation.opensearch.rest;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.opensearch.client.Response;
final class OpenSearchRestAttributesGetter final class OpenSearchRestAttributesGetter
implements DbClientAttributesGetter<OpenSearchRestRequest> { implements DbClientAttributesGetter<OpenSearchRestRequest, Response> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override
@ -49,4 +50,14 @@ final class OpenSearchRestAttributesGetter
public String getDbOperationName(OpenSearchRestRequest request) { public String getDbOperationName(OpenSearchRestRequest request) {
return request.getMethod(); return request.getMethod();
} }
@Nullable
@Override
public String getResponseStatus(@Nullable Response response, @Nullable Throwable error) {
if (response != null) {
int httpStatus = response.getStatusLine().getStatusCode();
return httpStatus >= 400 && httpStatus < 600 ? Integer.toString(httpStatus) : null;
}
return null;
}
} }

View File

@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.r2dbc.v1_0.internal;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter;
import io.r2dbc.spi.R2dbcException;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -15,7 +16,7 @@ import javax.annotation.Nullable;
* This class is internal and is hence not for public use. Its APIs are unstable and can change at * This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time. * any time.
*/ */
public enum R2dbcSqlAttributesGetter implements SqlClientAttributesGetter<DbExecution> { public enum R2dbcSqlAttributesGetter implements SqlClientAttributesGetter<DbExecution, Void> {
INSTANCE; INSTANCE;
@Override @Override
@ -47,4 +48,13 @@ public enum R2dbcSqlAttributesGetter implements SqlClientAttributesGetter<DbExec
public Collection<String> getRawQueryTexts(DbExecution request) { public Collection<String> getRawQueryTexts(DbExecution request) {
return singleton(request.getRawQueryText()); return singleton(request.getRawQueryText());
} }
@Nullable
@Override
public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) {
if (error instanceof R2dbcException) {
return ((R2dbcException) error).getSqlState();
}
return null;
}
} }

View File

@ -11,7 +11,8 @@ import java.util.Locale;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import redis.RedisCommand; import redis.RedisCommand;
final class RediscalaAttributesGetter implements DbClientAttributesGetter<RedisCommand<?, ?>> { final class RediscalaAttributesGetter
implements DbClientAttributesGetter<RedisCommand<?, ?>, Void> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -9,7 +9,7 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttribu
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
final class RedissonDbAttributesGetter implements DbClientAttributesGetter<RedissonRequest> { final class RedissonDbAttributesGetter implements DbClientAttributesGetter<RedissonRequest, Void> {
@SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues @SuppressWarnings("deprecation") // using deprecated DbSystemIncubatingValues
@Override @Override

View File

@ -8,7 +8,8 @@ package io.opentelemetry.javaagent.instrumentation.spymemcached;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class SpymemcachedAttributesGetter implements DbClientAttributesGetter<SpymemcachedRequest> { public class SpymemcachedAttributesGetter
implements DbClientAttributesGetter<SpymemcachedRequest, Object> {
@Override @Override
public String getDbSystem(SpymemcachedRequest spymemcachedRequest) { public String getDbSystem(SpymemcachedRequest spymemcachedRequest) {

View File

@ -10,6 +10,7 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
@ -24,17 +25,21 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@ -258,10 +263,22 @@ class SpymemcachedTest {
EXCEPTION_STACKTRACE, EXCEPTION_STACKTRACE,
val -> val.isInstanceOf(String.class)))) val -> val.isInstanceOf(String.class))))
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(
equalTo( withErrorType(
maybeStable(DB_SYSTEM), "net.spy.memcached.internal.CheckedOperationTimeoutException",
DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), equalTo(
equalTo(maybeStable(DB_OPERATION), "get")))); maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED),
equalTo(maybeStable(DB_OPERATION), "get")))));
}
private static List<AttributeAssertion> withErrorType(
String errorType, AttributeAssertion... assertions) {
List<AttributeAssertion> list = new ArrayList<>(Arrays.asList(assertions));
if (SemconvStability.emitStableDatabaseSemconv()) {
list.add(equalTo(ERROR_TYPE, errorType));
}
return list;
} }
@Test @Test
@ -879,10 +896,12 @@ class SpymemcachedTest {
.hasException( .hasException(
new IllegalArgumentException("Key is too long (maxlen = 250)")) new IllegalArgumentException("Key is too long (maxlen = 250)"))
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(
equalTo( withErrorType(
maybeStable(DB_SYSTEM), "java.lang.IllegalArgumentException",
DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), equalTo(
equalTo(maybeStable(DB_OPERATION), "decr")))); maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED),
equalTo(maybeStable(DB_OPERATION), "decr")))));
} }
@Test @Test
@ -965,10 +984,12 @@ class SpymemcachedTest {
.hasException( .hasException(
new IllegalArgumentException("Key is too long (maxlen = 250)")) new IllegalArgumentException("Key is too long (maxlen = 250)"))
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(
equalTo( withErrorType(
maybeStable(DB_SYSTEM), "java.lang.IllegalArgumentException",
DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), equalTo(
equalTo(maybeStable(DB_OPERATION), "incr")))); maybeStable(DB_SYSTEM),
DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED),
equalTo(maybeStable(DB_OPERATION), "incr")))));
} }
private static String key(String k) { private static String key(String k) {

View File

@ -13,7 +13,7 @@ import io.opentelemetry.semconv.incubating.DbIncubatingAttributes;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public enum VertxRedisClientAttributesGetter public enum VertxRedisClientAttributesGetter
implements DbClientAttributesGetter<VertxRedisClientRequest> { implements DbClientAttributesGetter<VertxRedisClientRequest, Void> {
INSTANCE; INSTANCE;
private static final RedisCommandSanitizer sanitizer = private static final RedisCommandSanitizer sanitizer =

View File

@ -8,11 +8,12 @@ package io.opentelemetry.javaagent.instrumentation.vertx.v4_0.sql;
import static java.util.Collections.singleton; import static java.util.Collections.singleton;
import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter; import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public enum VertxSqlClientAttributesGetter public enum VertxSqlClientAttributesGetter
implements SqlClientAttributesGetter<VertxSqlClientRequest> { implements SqlClientAttributesGetter<VertxSqlClientRequest, Void> {
INSTANCE; INSTANCE;
@Override @Override
@ -44,4 +45,22 @@ public enum VertxSqlClientAttributesGetter
public Collection<String> getRawQueryTexts(VertxSqlClientRequest request) { public Collection<String> getRawQueryTexts(VertxSqlClientRequest request) {
return singleton(request.getQueryText()); return singleton(request.getQueryText());
} }
@Nullable
@Override
public String getResponseStatus(@Nullable Void response, @Nullable Throwable error) {
try {
// loaded via reflection, because this class is not available in all versions that we support
Class<?> ex = Class.forName("io.vertx.pgclient.PgException");
if (ex.isInstance(error)) {
return (String) ex.getMethod("getCode").invoke(error);
}
} catch (ClassNotFoundException
| NoSuchMethodException
| IllegalAccessException
| InvocationTargetException e) {
return null;
}
return null;
}
} }

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.instrumentation.api.internal.SemconvStability.emi
import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable; import static io.opentelemetry.instrumentation.testing.junit.db.SemconvStabilityUtil.maybeStable;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static io.opentelemetry.semconv.ErrorAttributes.ERROR_TYPE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE; import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
@ -16,14 +17,17 @@ import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT; import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_RESPONSE_STATUS_CODE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SQL_TABLE; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_SQL_TABLE;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT;
import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_USER;
import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.api.internal.SemconvStability;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
import io.opentelemetry.sdk.testing.assertj.TraceAssert; import io.opentelemetry.sdk.testing.assertj.TraceAssert;
import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.sdk.trace.data.StatusData;
import io.vertx.core.Vertx; import io.vertx.core.Vertx;
@ -174,6 +178,20 @@ class VertxSqlClientTest {
latch.await(30, TimeUnit.SECONDS); latch.await(30, TimeUnit.SECONDS);
List<AttributeAssertion> assertions =
new ArrayList<>(
Arrays.asList(
equalTo(maybeStable(DB_NAME), DB),
equalTo(DB_USER, emitStableDatabaseSemconv() ? null : USER_DB),
equalTo(maybeStable(DB_STATEMENT), "invalid"),
equalTo(SERVER_ADDRESS, host),
equalTo(SERVER_PORT, port)));
if (SemconvStability.emitStableDatabaseSemconv()) {
assertions.add(equalTo(DB_RESPONSE_STATUS_CODE, "42601"));
assertions.add(equalTo(ERROR_TYPE, "io.vertx.pgclient.PgException"));
}
testing.waitAndAssertTraces( testing.waitAndAssertTraces(
trace -> trace ->
trace.hasSpansSatisfyingExactly( trace.hasSpansSatisfyingExactly(
@ -195,12 +213,7 @@ class VertxSqlClientTest {
satisfies( satisfies(
EXCEPTION_STACKTRACE, EXCEPTION_STACKTRACE,
val -> val.isInstanceOf(String.class)))) val -> val.isInstanceOf(String.class))))
.hasAttributesSatisfyingExactly( .hasAttributesSatisfyingExactly(assertions),
equalTo(maybeStable(DB_NAME), DB),
equalTo(DB_USER, emitStableDatabaseSemconv() ? null : USER_DB),
equalTo(maybeStable(DB_STATEMENT), "invalid"),
equalTo(SERVER_ADDRESS, host),
equalTo(SERVER_PORT, port)),
span -> span ->
span.hasName("callback") span.hasName("callback")
.hasKind(SpanKind.INTERNAL) .hasKind(SpanKind.INTERNAL)