diff --git a/java-client/src/main/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearch.java b/java-client/src/main/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearch.java index cc42e7a812..d0aed18feb 100644 --- a/java-client/src/main/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearch.java +++ b/java-client/src/main/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearch.java @@ -83,7 +83,12 @@ public class OpenTelemetryForElasticsearch implements Instrumentation { // Caching attributes keys to avoid unnecessary memory allocation private static final Map> attributesKeyCache = new ConcurrentHashMap<>(); - AttributeKeyTemplate PATH_PART_PREFIX = AttributeKeyTemplate.stringKeyTemplate("db.elasticsearch.path_parts"); + // The Elasticsearch semantic conventions renamed the "db.elasticsearch.path_parts." path parameter + // attributes to "db.operation.parameter.". Following the OpenTelemetry "otel.semconv-stability.opt-in" + // migration mechanism, the old prefix is emitted by default, the new one when "database" is opted in, and both + // when "database/dup" is opted in. + AttributeKeyTemplate OLD_PATH_PART_PREFIX = AttributeKeyTemplate.stringKeyTemplate("db.elasticsearch.path_parts"); + AttributeKeyTemplate NEW_PATH_PART_PREFIX = AttributeKeyTemplate.stringKeyTemplate("db.operation.parameter"); // these reflect the config options in the OTel Java agent private static final boolean INSTRUMENTATION_ENABLED = Boolean.parseBoolean( @@ -94,6 +99,24 @@ public class OpenTelemetryForElasticsearch implements Instrumentation { ConfigUtil.getConfigOption("otel.instrumentation.elasticsearch.capture-search-query", "false") ); + private static final String SEMCONV_STABILITY_OPT_IN = + ConfigUtil.getConfigOption("otel.semconv-stability.opt-in", ""); + + private static final boolean EMIT_STABLE_DATABASE_ATTRIBUTES = + hasOptIn("database") || hasOptIn("database/dup"); + + private static final boolean EMIT_OLD_DATABASE_ATTRIBUTES = + !EMIT_STABLE_DATABASE_ATTRIBUTES || hasOptIn("database/dup"); + + private static boolean hasOptIn(String value) { + for (String token : SEMCONV_STABILITY_OPT_IN.split(",")) { + if (token.trim().equals(value)) { + return true; + } + } + return false; + } + private static final Log logger = LogFactory.getLog(OpenTelemetryForElasticsearch.class); private final Tracer tracer; @@ -110,6 +133,11 @@ public class OpenTelemetryForElasticsearch implements Instrumentation { * {@code OTEL_INSTRUMENTATION_ELASTICSEARCH_CAPTURE_SEARCH_QUERY} environment variable: if {@code true} the request body * of search requests will be captured. Defaults to {@code false}. * + *
  • {@code otel.semconv-stability.opt-in} system property or {@code OTEL_SEMCONV_STABILITY_OPT_IN} + * environment variable: controls the migration to the stable database semantic conventions. + * This flag only affects the path parameter attributes; the other database attributes emitted by this client + * already follow the stable conventions. + *
  • * * * @return an instrumentation, or {@code null} if instrumentation is disabled or no OTel agent has been configured. @@ -194,8 +222,14 @@ OTelContext(TRequest request, Endpoint endpoint) { span.setAttribute(HTTP_REQUEST_METHOD, endpoint.method(request)); for (Map.Entry pathParamEntry : endpoint.pathParameters(request).entrySet()) { - AttributeKey attributeKey = PATH_PART_PREFIX.getAttributeKey(pathParamEntry.getKey()); - span.setAttribute(attributeKey, pathParamEntry.getValue()); + if (EMIT_OLD_DATABASE_ATTRIBUTES) { + span.setAttribute(OLD_PATH_PART_PREFIX.getAttributeKey(pathParamEntry.getKey()), + pathParamEntry.getValue()); + } + if (EMIT_STABLE_DATABASE_ATTRIBUTES) { + span.setAttribute(NEW_PATH_PART_PREFIX.getAttributeKey(pathParamEntry.getKey()), + pathParamEntry.getValue()); + } } } } catch (RuntimeException e) { diff --git a/java-client/src/test/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearchTest.java b/java-client/src/test/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearchTest.java index 1faea82b6d..86b4c685c0 100644 --- a/java-client/src/test/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearchTest.java +++ b/java-client/src/test/java/co/elastic/clients/transport/instrumentation/OpenTelemetryForElasticsearchTest.java @@ -196,8 +196,10 @@ public void testGetRequest() throws IOException, InterruptedException { Assertions.assertEquals(httpServer.getAddress().getHostString(), span.getAttributes().get(ServerAttributes.SERVER_ADDRESS)); Assertions.assertEquals(httpServer.getAddress().getPort(), span.getAttributes().get(ServerAttributes.SERVER_PORT)); - // Path parts + // Path parts: by default (no otel.semconv-stability.opt-in) the deprecated prefix is emitted and the + // stable "db.operation.parameter.*" prefix is not, preserving backwards compatibility. Assertions.assertEquals(DOC_ID, span.getAttributes().get(AttributeKey.stringKey("db.elasticsearch.path_parts.id"))); + Assertions.assertNull(span.getAttributes().get(AttributeKey.stringKey("db.operation.parameter.id"))); } @Test