From 99d6930ae58daaa31610d25ae5521e40a878d056 Mon Sep 17 00:00:00 2001 From: Luke Zhang Date: Fri, 22 Aug 2025 16:05:48 -0700 Subject: [PATCH 1/3] [semconv]: Add support for new formal DB semantic convention keys ADOT Java currently uses some deprecated incubating semconv keys. This PR adds support for the newly introduced formal semconv keys that replace them, while maintaining backward compatibility by falling back to the legacy keys when necessary. **Deprecated keys:** - DB_NAME - DB_OPERATION - DB_STATEMENT - DB_SYSTEM (Reference: https://github.com/open-telemetry/semantic-conventions-java/blob/release/v1.34.0/semconv-incubating/src/main/java/io/opentelemetry/semconv/incubating/DbIncubatingAttributes.java#L322-L327) **New keys:** - DB_NAMESPACE - DB_OPERATION_NAME - DB_QUERY_TEXT - DB_SYSTEM_NAME **Tests performed:** - Unit tests: `./gradlew build test` - Smoke/contract tests: `./gradlew appsignals-tests:contract-tests:contractTests` - Manual E2E test with SpringBoot sample app. --- .../AwsMetricAttributeGenerator.java | 43 +++++++++++-------- .../providers/AwsSpanMetricsProcessor.java | 10 ++--- .../providers/AwsSpanProcessingUtil.java | 9 ++-- .../AwsMetricAttributeGeneratorTest.java | 31 +++++++++++++ 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java index 2d913e0269..0f1613a136 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java @@ -15,6 +15,10 @@ package software.amazon.opentelemetry.javaagent.providers; +import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE; +import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_TEXT; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; @@ -26,7 +30,6 @@ // https://github.com/open-telemetry/semantic-conventions-java/blob/release/v1.34.0/semconv-incubating/src/main/java/io/opentelemetry/semconv/incubating/DbIncubatingAttributes.java#L322-L327 // They have been replaced with new keys: // https://github.com/open-telemetry/semantic-conventions-java/blob/release/v1.34.0/semconv/src/main/java/io/opentelemetry/semconv/DbAttributes.java#L77 -// TODO: Supporting new keys. Cannot do this now as new keys are not available in OTel Agent 2.11. // TODO: Delete deprecated keys once they no longer exist in binding version of the upstream code. import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_CONNECTION_STRING; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_NAME; @@ -93,6 +96,7 @@ import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_OPERATION; import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_OPERATION; import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.UNKNOWN_REMOTE_SERVICE; +import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.getKeyValueWithFallback; import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isAwsSDKSpan; import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isDBSpan; import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isKeyPresent; @@ -285,11 +289,12 @@ private static void setRemoteServiceAndOperation(SpanData span, AttributesBuilde remoteOperation = getRemoteOperation(span, RPC_METHOD); } else if (isDBSpan(span)) { - remoteService = getRemoteService(span, DB_SYSTEM); - if (isKeyPresent(span, DB_OPERATION)) { - remoteOperation = getRemoteOperation(span, DB_OPERATION); + remoteService = getRemoteServiceWithFallback(span, DB_SYSTEM_NAME, DB_SYSTEM); + if (isKeyPresentWithFallback(span, DB_OPERATION_NAME, DB_OPERATION)) { + remoteOperation = getRemoteOperationWithFallback(span, DB_OPERATION_NAME, DB_OPERATION); } else { - remoteOperation = getDBStatementRemoteOperation(span, DB_STATEMENT); + String dbStatement = getKeyValueWithFallback(span, DB_QUERY_TEXT, DB_STATEMENT); + remoteOperation = getDBStatementRemoteOperation(span, dbStatement); } } else if (isKeyPresent(span, FAAS_INVOKED_NAME) || isKeyPresent(span, FAAS_TRIGGER)) { remoteService = getRemoteService(span, FAAS_INVOKED_NAME); @@ -349,10 +354,7 @@ private static void setRemoteEnvironment(SpanData span, AttributesBuilder builde private static String generateRemoteOperation(SpanData span) { String remoteOperation = UNKNOWN_REMOTE_OPERATION; if (isKeyPresent(span, URL_FULL) || isKeyPresent(span, HTTP_URL)) { - String httpUrl = - isKeyPresent(span, URL_FULL) - ? span.getAttributes().get(URL_FULL) - : span.getAttributes().get(HTTP_URL); + String httpUrl = getKeyValueWithFallback(span, URL_FULL, HTTP_URL); try { URL url; if (httpUrl != null) { @@ -363,11 +365,8 @@ private static String generateRemoteOperation(SpanData span) { logger.log(Level.FINEST, "invalid http.url attribute: ", httpUrl); } } - if (isKeyPresent(span, HTTP_REQUEST_METHOD) || isKeyPresent(span, HTTP_METHOD)) { - String httpMethod = - isKeyPresent(span, HTTP_REQUEST_METHOD) - ? span.getAttributes().get(HTTP_REQUEST_METHOD) - : span.getAttributes().get(HTTP_METHOD); + if (isKeyPresentWithFallback(span, HTTP_REQUEST_METHOD, HTTP_METHOD)) { + String httpMethod = getKeyValueWithFallback(span, HTTP_REQUEST_METHOD, HTTP_METHOD); remoteOperation = httpMethod + " " + remoteOperation; } if (remoteOperation.equals(UNKNOWN_REMOTE_OPERATION)) { @@ -791,7 +790,7 @@ private static Optional getSnsResourceNameFromArn(Optional strin * provided. */ private static Optional getDbConnection(SpanData span) { - String dbName = span.getAttributes().get(DB_NAME); + String dbName = getKeyValueWithFallback(span, DB_NAMESPACE, DB_NAME); Optional dbConnection = Optional.empty(); if (isKeyPresent(span, SERVER_ADDRESS)) { @@ -949,6 +948,15 @@ private static String getRemoteService(SpanData span, AttributeKey remot return remoteService; } + static String getRemoteServiceWithFallback( + SpanData span, AttributeKey remoteSvcKey, AttributeKey remoteSvcFallbackKey) { + String remoteService = span.getAttributes().get(remoteSvcKey); + if (remoteService == null) { + return getRemoteService(span, remoteSvcFallbackKey); + } + return remoteService; + } + private static String getRemoteOperation(SpanData span, AttributeKey remoteOperationKey) { String remoteOperation = span.getAttributes().get(remoteOperationKey); if (remoteOperation == null) { @@ -972,9 +980,8 @@ static String getRemoteOperationWithFallback( * statement and compare to a regex list of known SQL keywords. The substring length is determined * by the longest known SQL keywords. */ - private static String getDBStatementRemoteOperation( - SpanData span, AttributeKey remoteOperationKey) { - String remoteOperation = span.getAttributes().get(remoteOperationKey); + private static String getDBStatementRemoteOperation(SpanData span, String dbStatement) { + String remoteOperation = dbStatement; if (remoteOperation == null) { remoteOperation = UNKNOWN_REMOTE_OPERATION; } diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanMetricsProcessor.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanMetricsProcessor.java index 37436f5d3b..b8fbba61ef 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanMetricsProcessor.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanMetricsProcessor.java @@ -18,7 +18,7 @@ import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; import static io.opentelemetry.semconv.incubating.HttpIncubatingAttributes.HTTP_STATUS_CODE; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_SERVICE; -import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isKeyPresent; +import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.getKeyValueWithFallback; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.metrics.DoubleHistogram; @@ -136,12 +136,8 @@ public boolean isEndRequired() { // possible except for the throttle // https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/awsxrayexporter/internal/translator/cause.go#L121-L160 private void recordErrorOrFault(SpanData spanData, Attributes attributes) { - Long httpStatusCode = null; - if (isKeyPresent(spanData, HTTP_RESPONSE_STATUS_CODE)) { - httpStatusCode = spanData.getAttributes().get(HTTP_RESPONSE_STATUS_CODE); - } else if (isKeyPresent(spanData, HTTP_STATUS_CODE)) { - httpStatusCode = spanData.getAttributes().get(HTTP_STATUS_CODE); - } + Long httpStatusCode = + getKeyValueWithFallback(spanData, HTTP_RESPONSE_STATUS_CODE, HTTP_STATUS_CODE); StatusCode statusCode = spanData.getStatus().getStatusCode(); if (httpStatusCode == null) { diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanProcessingUtil.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanProcessingUtil.java index 4da4d80e3d..ab92d050be 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanProcessingUtil.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsSpanProcessingUtil.java @@ -15,6 +15,9 @@ package software.amazon.opentelemetry.javaagent.providers; +import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME; +import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_TEXT; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.UrlAttributes.URL_PATH; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_OPERATION; import static io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DB_STATEMENT; @@ -295,9 +298,9 @@ private static String generateIngressOperation(SpanData span) { // Check if the current Span adheres to database semantic conventions static boolean isDBSpan(SpanData span) { - return isKeyPresent(span, DB_SYSTEM) - || isKeyPresent(span, DB_OPERATION) - || isKeyPresent(span, DB_STATEMENT); + return isKeyPresentWithFallback(span, DB_SYSTEM_NAME, DB_SYSTEM) + || isKeyPresentWithFallback(span, DB_OPERATION_NAME, DB_OPERATION) + || isKeyPresentWithFallback(span, DB_QUERY_TEXT, DB_STATEMENT); } static boolean isLambdaServerSpan(ReadableSpan span) { diff --git a/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java index 81667f6313..e3d438e10f 100644 --- a/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java +++ b/awsagentprovider/src/test/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGeneratorTest.java @@ -15,6 +15,7 @@ package software.amazon.opentelemetry.javaagent.providers; +import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME; import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; import static io.opentelemetry.semconv.NetworkAttributes.NETWORK_PEER_ADDRESS; @@ -1792,4 +1793,34 @@ public void testBothMetricsWhenLocalRootConsumerProcess() { assertThat(attributeMap.get(SERVICE_METRIC)).isEqualTo(serviceAttributes); assertThat(attributeMap.get(DEPENDENCY_METRIC)).isEqualTo(dependencyAttributes); } + + @Test + public void testGetRemoteServiceWithFallback_PrimaryKeyPresent() { + mockAttribute(DB_SYSTEM_NAME, "mysql"); + mockAttribute(DB_SYSTEM, "postgresql"); + String result = + AwsMetricAttributeGenerator.getRemoteServiceWithFallback( + spanDataMock, DB_SYSTEM_NAME, DB_SYSTEM); + + assertThat(result).isEqualTo("mysql"); + } + + @Test + public void testGetRemoteServiceWithFallback_FallbackKeyPresent() { + mockAttribute(DB_SYSTEM, "postgresql"); + String result = + AwsMetricAttributeGenerator.getRemoteServiceWithFallback( + spanDataMock, DB_SYSTEM_NAME, DB_SYSTEM); + + assertThat(result).isEqualTo("postgresql"); + } + + @Test + public void testGetRemoteServiceWithFallback_BothKeysAbsent() { + String result = + AwsMetricAttributeGenerator.getRemoteServiceWithFallback( + spanDataMock, DB_SYSTEM_NAME, DB_SYSTEM); + + assertThat(result).isEqualTo(UNKNOWN_REMOTE_SERVICE); + } } From 1f9f544ac1cc48ebbab4b2861ff54eb3c36e0ac8 Mon Sep 17 00:00:00 2001 From: "Luke (GuangHui) Zhang" Date: Mon, 22 Sep 2025 17:04:58 -0700 Subject: [PATCH 2/3] Update awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java Co-authored-by: Thomas Pierce --- .../javaagent/providers/AwsMetricAttributeGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java index 3985ff0502..1f8e10188f 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java @@ -949,7 +949,7 @@ private static String getRemoteService(SpanData span, AttributeKey remot } static String getRemoteServiceWithFallback( - SpanData span, AttributeKey remoteSvcKey, AttributeKey remoteSvcFallbackKey) { + SpanData span, AttributeKey remoteServiceKey, AttributeKey remoteServiceFallbackKey) { String remoteService = span.getAttributes().get(remoteSvcKey); if (remoteService == null) { return getRemoteService(span, remoteSvcFallbackKey); From 5d81c714fbe74b2353ef44bc36d3ba28e72d0098 Mon Sep 17 00:00:00 2001 From: Luke Zhang Date: Mon, 22 Sep 2025 18:59:42 -0700 Subject: [PATCH 3/3] rename from remoteSvcKey to remoteServiceKey --- .../javaagent/providers/AwsMetricAttributeGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java index 1f8e10188f..7522ad8663 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsMetricAttributeGenerator.java @@ -950,9 +950,9 @@ private static String getRemoteService(SpanData span, AttributeKey remot static String getRemoteServiceWithFallback( SpanData span, AttributeKey remoteServiceKey, AttributeKey remoteServiceFallbackKey) { - String remoteService = span.getAttributes().get(remoteSvcKey); + String remoteService = span.getAttributes().get(remoteServiceKey); if (remoteService == null) { - return getRemoteService(span, remoteSvcFallbackKey); + return getRemoteService(span, remoteServiceFallbackKey); } return remoteService; }