diff --git a/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/internal/DbResponseStatusUtil.java b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/internal/DbResponseStatusUtil.java new file mode 100644 index 000000000000..9678218817c9 --- /dev/null +++ b/instrumentation-api-incubator/src/main/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/internal/DbResponseStatusUtil.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.api.incubator.semconv.db.internal; + +import javax.annotation.Nullable; + +/** + * This class is internal and experimental. Its APIs are unstable and can change at any time. Its + * APIs (or a version of them) may be promoted to the public stable API in the future, but no + * guarantees are made. + */ +public final class DbResponseStatusUtil { + private DbResponseStatusUtil() {} + + @Nullable + public static String dbResponseStatusCode(int responseStatusCode) { + return isError(responseStatusCode) ? Integer.toString(responseStatusCode) : null; + } + + private static boolean isError(int responseStatusCode) { + return responseStatusCode >= 400 + || + // invalid status code, does not exist + responseStatusCode < 100; + } +} diff --git a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpStatusCodeConverter.java b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpStatusCodeConverter.java index eb1724d69d35..740c4280422a 100644 --- a/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpStatusCodeConverter.java +++ b/instrumentation-api/src/main/java/io/opentelemetry/instrumentation/api/semconv/http/HttpStatusCodeConverter.java @@ -12,7 +12,7 @@ enum HttpStatusCodeConverter { boolean isError(int responseStatusCode) { return responseStatusCode >= 500 || - // invalid status code, does not exists + // invalid status code, does not exist responseStatusCode < 100; } }, @@ -21,7 +21,7 @@ boolean isError(int responseStatusCode) { boolean isError(int responseStatusCode) { return responseStatusCode >= 400 || - // invalid status code, does not exists + // invalid status code, does not exist responseStatusCode < 100; } }; diff --git a/instrumentation/elasticsearch/elasticsearch-rest-common-5.0/library/src/main/java/io/opentelemetry/instrumentation/elasticsearch/rest/common/v5_0/internal/ElasticsearchDbAttributesGetter.java b/instrumentation/elasticsearch/elasticsearch-rest-common-5.0/library/src/main/java/io/opentelemetry/instrumentation/elasticsearch/rest/common/v5_0/internal/ElasticsearchDbAttributesGetter.java index 8e8cf35835ca..26c788f00f31 100644 --- a/instrumentation/elasticsearch/elasticsearch-rest-common-5.0/library/src/main/java/io/opentelemetry/instrumentation/elasticsearch/rest/common/v5_0/internal/ElasticsearchDbAttributesGetter.java +++ b/instrumentation/elasticsearch/elasticsearch-rest-common-5.0/library/src/main/java/io/opentelemetry/instrumentation/elasticsearch/rest/common/v5_0/internal/ElasticsearchDbAttributesGetter.java @@ -5,6 +5,7 @@ package io.opentelemetry.instrumentation.elasticsearch.rest.common.v5_0.internal; +import static io.opentelemetry.instrumentation.api.incubator.semconv.db.internal.DbResponseStatusUtil.dbResponseStatusCode; import static java.util.logging.Level.FINE; import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; @@ -96,10 +97,6 @@ public String getDbOperationName(ElasticsearchRestRequest request) { @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; + return response != null ? dbResponseStatusCode(response.getStatusLine().getStatusCode()) : null; } } diff --git a/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java index 6a572e8ca234..172a7883fc72 100644 --- a/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java +++ b/instrumentation/opensearch/opensearch-rest-common/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/opensearch/rest/OpenSearchRestAttributesGetter.java @@ -5,6 +5,8 @@ package io.opentelemetry.javaagent.instrumentation.opensearch.rest; +import static io.opentelemetry.instrumentation.api.incubator.semconv.db.internal.DbResponseStatusUtil.dbResponseStatusCode; + import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import javax.annotation.Nullable; @@ -54,10 +56,6 @@ public String getDbOperationName(OpenSearchRestRequest request) { @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; + return response != null ? dbResponseStatusCode(response.getStatusLine().getStatusCode()) : null; } } diff --git a/instrumentation/spymemcached-2.12/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedTest.java b/instrumentation/spymemcached-2.12/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedTest.java index 4b5a3231474e..0408bbdd19af 100644 --- a/instrumentation/spymemcached-2.12/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedTest.java +++ b/instrumentation/spymemcached-2.12/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spymemcached/SpymemcachedTest.java @@ -29,17 +29,14 @@ import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension; import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import io.opentelemetry.sdk.testing.assertj.AttributeAssertion; import io.opentelemetry.sdk.trace.data.StatusData; import io.opentelemetry.semconv.incubating.DbIncubatingAttributes; import java.net.InetSocketAddress; import java.time.Duration; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; @@ -263,22 +260,15 @@ void getTimeout() throws InterruptedException { EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class)))) .hasAttributesSatisfyingExactly( - withErrorType( - "net.spy.memcached.internal.CheckedOperationTimeoutException", - equalTo( - maybeStable(DB_SYSTEM), - DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), - equalTo(maybeStable(DB_OPERATION), "get"))))); - } - - private static List withErrorType( - String errorType, AttributeAssertion... assertions) { - List list = new ArrayList<>(Arrays.asList(assertions)); - - if (SemconvStability.emitStableDatabaseSemconv()) { - list.add(equalTo(ERROR_TYPE, errorType)); - } - return list; + equalTo( + ERROR_TYPE, + SemconvStability.emitStableDatabaseSemconv() + ? "net.spy.memcached.internal.CheckedOperationTimeoutException" + : null), + equalTo( + maybeStable(DB_SYSTEM), + DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), + equalTo(maybeStable(DB_OPERATION), "get")))); } @Test @@ -896,12 +886,15 @@ void decrException() { .hasException( new IllegalArgumentException("Key is too long (maxlen = 250)")) .hasAttributesSatisfyingExactly( - withErrorType( - "java.lang.IllegalArgumentException", - equalTo( - maybeStable(DB_SYSTEM), - DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), - equalTo(maybeStable(DB_OPERATION), "decr"))))); + equalTo( + ERROR_TYPE, + SemconvStability.emitStableDatabaseSemconv() + ? "java.lang.IllegalArgumentException" + : null), + equalTo( + maybeStable(DB_SYSTEM), + DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), + equalTo(maybeStable(DB_OPERATION), "decr")))); } @Test @@ -984,12 +977,15 @@ void incrException() { .hasException( new IllegalArgumentException("Key is too long (maxlen = 250)")) .hasAttributesSatisfyingExactly( - withErrorType( - "java.lang.IllegalArgumentException", - equalTo( - maybeStable(DB_SYSTEM), - DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), - equalTo(maybeStable(DB_OPERATION), "incr"))))); + equalTo( + ERROR_TYPE, + SemconvStability.emitStableDatabaseSemconv() + ? "java.lang.IllegalArgumentException" + : null), + equalTo( + maybeStable(DB_SYSTEM), + DbIncubatingAttributes.DbSystemIncubatingValues.MEMCACHED), + equalTo(maybeStable(DB_OPERATION), "incr")))); } private static String key(String k) { diff --git a/instrumentation/vertx/vertx-sql-client-4.0/javaagent/build.gradle.kts b/instrumentation/vertx/vertx-sql-client-4.0/javaagent/build.gradle.kts index eff4457a3eec..4e2363c90876 100644 --- a/instrumentation/vertx/vertx-sql-client-4.0/javaagent/build.gradle.kts +++ b/instrumentation/vertx/vertx-sql-client-4.0/javaagent/build.gradle.kts @@ -12,13 +12,14 @@ muzzle { } dependencies { - library("io.vertx:vertx-sql-client:4.0.0") - compileOnly("io.vertx:vertx-codegen:4.0.0") + val version = "4.0.0" + library("io.vertx:vertx-sql-client:$version") + compileOnly("io.vertx:vertx-codegen:$version") testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent")) - testLibrary("io.vertx:vertx-pg-client:4.0.0") - testLibrary("io.vertx:vertx-codegen:4.0.0") + testLibrary("io.vertx:vertx-pg-client:$version") + testLibrary("io.vertx:vertx-codegen:$version") } tasks { diff --git a/instrumentation/vertx/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientAttributesGetter.java b/instrumentation/vertx/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientAttributesGetter.java index 63a4e04cfd21..8064e874c783 100644 --- a/instrumentation/vertx/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientAttributesGetter.java +++ b/instrumentation/vertx/vertx-sql-client-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/vertx/v4_0/sql/VertxSqlClientAttributesGetter.java @@ -9,13 +9,20 @@ import io.opentelemetry.instrumentation.api.incubator.semconv.db.SqlClientAttributesGetter; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.function.Function; import javax.annotation.Nullable; public enum VertxSqlClientAttributesGetter implements SqlClientAttributesGetter { INSTANCE; + private static final List> responseStatusExtractors = + createResponseStatusExtractors(); + @Override public String getDbSystem(VertxSqlClientRequest request) { return null; @@ -49,18 +56,41 @@ public Collection getRawQueryTexts(VertxSqlClientRequest request) { @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); + for (Function extractor : responseStatusExtractors) { + String status = extractor.apply((Exception) error); + if (status != null) { + return status; } - } catch (ClassNotFoundException - | NoSuchMethodException - | IllegalAccessException - | InvocationTargetException e) { - return null; } return null; } + + private static List> createResponseStatusExtractors() { + return Arrays.asList( + responseStatusExtractor("io.vertx.sqlclient.DatabaseException", "getSqlState"), + // older version only have this method + responseStatusExtractor("io.vertx.pgclient.PgException", "getCode")); + } + + private static Function responseStatusExtractor( + String className, String methodName) { + try { + // loaded via reflection, because this class is not available in all versions that we support + Class exClass = Class.forName(className); + Method method = exClass.getDeclaredMethod(methodName); + + return (error) -> { + if (exClass.isInstance(error)) { + try { + return String.valueOf(method.invoke(error)); // can be String or int + } catch (IllegalAccessException | InvocationTargetException e) { + return null; + } + } + return null; + }; + } catch (ClassNotFoundException | NoSuchMethodException e) { + return (error) -> null; + } + } }