diff --git a/.github/patches/opentelemetry-java-contrib.patch b/.github/patches/opentelemetry-java-contrib.patch new file mode 100644 index 0000000000..65a6f8b780 --- /dev/null +++ b/.github/patches/opentelemetry-java-contrib.patch @@ -0,0 +1,237 @@ +diff --git a/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java b/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java +index 1ef8abf..ef84f35 100644 +--- a/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java ++++ b/aws-xray/src/main/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplier.java +@@ -35,6 +35,11 @@ final class SamplingRuleApplier { + + private static final Map XRAY_CLOUD_PLATFORM; + ++ private static final AttributeKey URL_PATH = AttributeKey.stringKey("url.path"); ++ private static final AttributeKey URL_FULL = AttributeKey.stringKey("url.full"); ++ private static final AttributeKey HTTP_REQUEST_METHOD = ++ AttributeKey.stringKey("http.request.method"); ++ + static { + Map xrayCloudPlatform = new HashMap<>(); + xrayCloudPlatform.put(ResourceAttributes.CloudPlatformValues.AWS_EC2, "AWS::EC2::Instance"); +@@ -162,11 +167,14 @@ final class SamplingRuleApplier { + String host = null; + + for (Map.Entry, Object> entry : attributes.asMap().entrySet()) { +- if (entry.getKey().equals(SemanticAttributes.HTTP_TARGET)) { ++ if (entry.getKey().equals(SemanticAttributes.HTTP_TARGET) ++ || entry.getKey().equals(URL_PATH)) { + httpTarget = (String) entry.getValue(); +- } else if (entry.getKey().equals(SemanticAttributes.HTTP_URL)) { ++ } else if (entry.getKey().equals(SemanticAttributes.HTTP_URL) ++ || entry.getKey().equals(URL_FULL)) { + httpUrl = (String) entry.getValue(); +- } else if (entry.getKey().equals(SemanticAttributes.HTTP_METHOD)) { ++ } else if (entry.getKey().equals(SemanticAttributes.HTTP_METHOD) ++ || entry.getKey().equals(HTTP_REQUEST_METHOD)) { + httpMethod = (String) entry.getValue(); + } else if (entry.getKey().equals(SemanticAttributes.NET_HOST_NAME)) { + host = (String) entry.getValue(); +diff --git a/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java b/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java +index 6bb6e82..55dabbd 100644 +--- a/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java ++++ b/aws-xray/src/test/java/io/opentelemetry/contrib/awsxray/SamplingRuleApplierTest.java +@@ -42,6 +42,11 @@ class SamplingRuleApplierTest { + + private static final String CLIENT_ID = "test-client-id"; + ++ private static final AttributeKey URL_PATH = AttributeKey.stringKey("url.path"); ++ private static final AttributeKey URL_FULL = AttributeKey.stringKey("url.full"); ++ private static final AttributeKey HTTP_REQUEST_METHOD = ++ AttributeKey.stringKey("http.request.method"); ++ + @Nested + @SuppressWarnings("ClassCanBeStatic") + class ExactMatch { +@@ -68,6 +73,15 @@ class SamplingRuleApplierTest { + .put(AttributeKey.longKey("speed"), 10) + .build(); + ++ private final Attributes newSemCovAttributes = ++ Attributes.builder() ++ .put(HTTP_REQUEST_METHOD, "GET") ++ .put(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io") ++ .put(URL_PATH, "/instrument-me") ++ .put(AttributeKey.stringKey("animal"), "cat") ++ .put(AttributeKey.longKey("speed"), 10) ++ .build(); ++ + // FixedRate set to 1.0 in rule and no reservoir + @Test + void fixedRateAlwaysSample() { +@@ -116,6 +130,21 @@ class SamplingRuleApplierTest { + .isTrue(); + } + ++ @Test ++ void matchesURLFullNewSemCov() { ++ assertThat(applier.matches(newSemCovAttributes, resource)).isTrue(); ++ ++ // http.url works too ++ assertThat( ++ applier.matches( ++ attributes.toBuilder() ++ .remove(URL_FULL) ++ .put(URL_FULL, "scheme://host:port/instrument-me") ++ .build(), ++ resource)) ++ .isTrue(); ++ } ++ + @Test + void serviceNameNotMatch() { + assertThat( +@@ -137,6 +166,13 @@ class SamplingRuleApplierTest { + assertThat(applier.matches(attributes, resource)).isFalse(); + } + ++ @Test ++ void methodNewSemCovNotMatch() { ++ Attributes attributes = ++ this.newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "POST").build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ } ++ + @Test + void hostNotMatch() { + // Replacing dot with character makes sure we're not accidentally treating dot as regex +@@ -178,6 +214,34 @@ class SamplingRuleApplierTest { + assertThat(applier.matches(attributes, resource)).isFalse(); + } + ++ @Test ++ void pathNewSemCovNotMatch() { ++ Attributes attributes = ++ this.newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-you").build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ attributes = ++ this.newSemCovAttributes.toBuilder() ++ .remove(URL_PATH) ++ .put(URL_FULL, "scheme://host:port/instrument-you") ++ .build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ attributes = ++ this.newSemCovAttributes.toBuilder() ++ .remove(URL_PATH) ++ .put(URL_FULL, "scheme://host:port") ++ .build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ ++ // Correct path, but we ignore anyways since the URL is malformed per spec, scheme is always ++ // present. ++ attributes = ++ this.newSemCovAttributes.toBuilder() ++ .remove(URL_PATH) ++ .put(URL_FULL, "host:port/instrument-me") ++ .build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ } ++ + @Test + void attributeNotMatch() { + Attributes attributes = +@@ -243,6 +307,15 @@ class SamplingRuleApplierTest { + .put(AttributeKey.longKey("speed"), 10) + .build(); + ++ private final Attributes newSemCovAttributes = ++ Attributes.builder() ++ .put(HTTP_REQUEST_METHOD, "GET") ++ .put(SemanticAttributes.NET_HOST_NAME, "opentelemetry.io") ++ .put(URL_PATH, "/instrument-me?foo=bar&cat=meow") ++ .put(AttributeKey.stringKey("animal"), "cat") ++ .put(AttributeKey.longKey("speed"), 10) ++ .build(); ++ + // FixedRate set to 0.0 in rule and no reservoir + @Test + void fixedRateNeverSample() { +@@ -329,6 +402,26 @@ class SamplingRuleApplierTest { + assertThat(applier.matches(attributes, resource)).isFalse(); + } + ++ @Test ++ void newSemCovMethodMatches() { ++ Attributes attributes = ++ this.newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "BADGETGOOD").build(); ++ assertThat(applier.matches(attributes, resource)).isTrue(); ++ attributes = newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "BADGET").build(); ++ assertThat(applier.matches(attributes, resource)).isTrue(); ++ attributes = newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "GETGET").build(); ++ assertThat(applier.matches(attributes, resource)).isTrue(); ++ } ++ ++ @Test ++ void newSemCovMethodNotMatch() { ++ Attributes attributes = ++ newSemCovAttributes.toBuilder().put(HTTP_REQUEST_METHOD, "POST").build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ attributes = removeAttribute(newSemCovAttributes, HTTP_REQUEST_METHOD); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ } ++ + @Test + void hostMatches() { + Attributes attributes = +@@ -410,6 +503,29 @@ class SamplingRuleApplierTest { + assertThat(applier.matches(attributes, resource)).isFalse(); + } + ++ @Test ++ void pathNewSemCovMatches() { ++ Attributes attributes = ++ newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-me?foo=bar&cat=").build(); ++ assertThat(applier.matches(attributes, resource)).isTrue(); ++ // Deceptive question mark, it's actually a wildcard :-) ++ attributes = ++ newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-meafoo=bar&cat=").build(); ++ assertThat(applier.matches(attributes, resource)).isTrue(); ++ } ++ ++ @Test ++ void pathNewSemCovNotMatch() { ++ Attributes attributes = ++ newSemCovAttributes.toBuilder().put(URL_PATH, "/instrument-mea?foo=bar&cat=").build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ attributes = ++ newSemCovAttributes.toBuilder().put(URL_PATH, "foo/instrument-meafoo=bar&cat=").build(); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ attributes = removeAttribute(newSemCovAttributes, URL_PATH); ++ assertThat(applier.matches(attributes, resource)).isFalse(); ++ } ++ + @Test + void attributeMatches() { + Attributes attributes = +diff --git a/disk-buffering/build.gradle.kts b/disk-buffering/build.gradle.kts +index 041d2e9..e3d60f4 100644 +--- a/disk-buffering/build.gradle.kts ++++ b/disk-buffering/build.gradle.kts +@@ -70,6 +70,10 @@ tasks.named("shadowJar") { + mustRunAfter("jar") + } + ++tasks.withType().configureEach { ++ dependsOn("shadowJar") ++} ++ + // The javadoc from wire's generated classes has errors that make the task that generates the "javadoc" artifact to fail. This + // makes the javadoc task to ignore those generated classes. + tasks.withType(Javadoc::class.java) { +diff --git a/version.gradle.kts b/version.gradle.kts +index acefcee..329b524 100644 +--- a/version.gradle.kts ++++ b/version.gradle.kts +@@ -1,5 +1,5 @@ +-val stableVersion = "1.39.0" +-val alphaVersion = "1.39.0-alpha" ++val stableVersion = "1.39.0-adot1" ++val alphaVersion = "1.39.0-alpha-adot1" + + allprojects { + if (findProperty("otel.stable") != "true") { diff --git a/.github/patches/opentelemetry-java-instrumentation.patch b/.github/patches/opentelemetry-java-instrumentation.patch index fedb7dd666..1de3294474 100644 --- a/.github/patches/opentelemetry-java-instrumentation.patch +++ b/.github/patches/opentelemetry-java-instrumentation.patch @@ -3573,8 +3573,8 @@ index a1cae43b4b..c1520e9947 100644 @@ -1,5 +1,5 @@ -val stableVersion = "2.11.0" -val alphaVersion = "2.11.0-alpha" -+val stableVersion = "2.11.0-adot1" -+val alphaVersion = "2.11.0-adot1-alpha" ++val stableVersion = "2.11.0-adot2" ++val alphaVersion = "2.11.0-adot2-alpha" allprojects { if (findProperty("otel.stable") != "true") { diff --git a/.github/patches/versions b/.github/patches/versions index 666c225554..be4e3bbcb6 100644 --- a/.github/patches/versions +++ b/.github/patches/versions @@ -1 +1,2 @@ OTEL_JAVA_INSTRUMENTATION_VERSION=v2.11.0 +OTEL_JAVA_CONTRIB_VERSION=v1.39.0 \ No newline at end of file diff --git a/.github/workflows/owasp.yml b/.github/workflows/owasp.yml index 89e4d7b0ff..ecf34587f2 100644 --- a/.github/workflows/owasp.yml +++ b/.github/workflows/owasp.yml @@ -97,7 +97,7 @@ jobs: id: high_scan_v2 uses: ./.github/actions/image_scan with: - image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-java:v2.10.0" + image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-java:v2.11.0" severity: 'CRITICAL,HIGH' - name: Perform low image scan on v2 @@ -105,7 +105,7 @@ jobs: id: low_scan_v2 uses: ./.github/actions/image_scan with: - image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-java:v2.10.0" + image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-java:v2.11.0" severity: 'MEDIUM,LOW,UNKNOWN' - name: Configure AWS Credentials for emitting metrics diff --git a/.github/workflows/release-udp-exporter.yml b/.github/workflows/release-udp-exporter.yml index 97c894c7fb..50b4d67065 100644 --- a/.github/workflows/release-udp-exporter.yml +++ b/.github/workflows/release-udp-exporter.yml @@ -65,4 +65,4 @@ jobs: --title "Release aws-distro-opentelemetry-xray-udp-span-exporter v${{ inputs.udp-exporter-version }}" \ --notes "Please refer to the [Changelog](https://github.com/aws-observability/aws-otel-java-instrumentation/blob/main/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/CHANGELOG.md) for more details" \ --draft \ - "v${{ inputs.udp-exporter-version }}" \ No newline at end of file + "aws-distro-opentelemetry-xray-udp-span-exporter/v${{ inputs.udp-exporter-version }}" diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java index 1df824626c..d6bd420475 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsApplicationSignalsCustomizerProvider.java @@ -77,6 +77,8 @@ public final class AwsApplicationSignalsCustomizerProvider implements AutoConfigurationCustomizerProvider { static final String AWS_LAMBDA_FUNCTION_NAME_CONFIG = "AWS_LAMBDA_FUNCTION_NAME"; + static final String LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT = + "LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT"; private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration.ofMinutes(1); private static final Logger logger = diff --git a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java index f9791a31ee..75596460d9 100644 --- a/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java +++ b/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsAttributeKeys.java @@ -32,6 +32,9 @@ private AwsAttributeKeys() {} static final AttributeKey AWS_REMOTE_SERVICE = AttributeKey.stringKey("aws.remote.service"); + static final AttributeKey AWS_REMOTE_ENVIRONMENT = + AttributeKey.stringKey("aws.remote.environment"); + static final AttributeKey AWS_REMOTE_OPERATION = AttributeKey.stringKey("aws.remote.operation"); @@ -64,6 +67,9 @@ private AwsAttributeKeys() {} static final AttributeKey AWS_SECRET_ARN = AttributeKey.stringKey("aws.secretsmanager.secret.arn"); + static final AttributeKey AWS_LAMBDA_NAME = + AttributeKey.stringKey("aws.lambda.function.name"); + static final AttributeKey AWS_LAMBDA_ARN = AttributeKey.stringKey("aws.lambda.function.arn"); 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 564d0e890d..2e138f6e35 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 @@ -45,6 +45,7 @@ import static io.opentelemetry.semconv.SemanticAttributes.SERVER_SOCKET_ADDRESS; import static io.opentelemetry.semconv.SemanticAttributes.SERVER_SOCKET_PORT; import static io.opentelemetry.semconv.SemanticAttributes.URL_FULL; +import static software.amazon.opentelemetry.javaagent.providers.AwsApplicationSignalsCustomizerProvider.LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_AGENT_ID; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_BUCKET_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER; @@ -52,12 +53,15 @@ import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ARN; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ID; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_KNOWLEDGE_BASE_ID; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_ARN; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_RESOURCE_ID; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_OPERATION; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_SERVICE; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_URL; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_DB_USER; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_ENVIRONMENT; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_OPERATION; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_IDENTIFIER; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_TYPE; @@ -129,6 +133,9 @@ final class AwsMetricAttributeGenerator implements MetricAttributeGenerator { private static final String NORMALIZED_SECRETSMANAGER_SERVICE_NAME = "AWS::SecretsManager"; private static final String NORMALIZED_LAMBDA_SERVICE_NAME = "AWS::Lambda"; + // Constants for Lambda operations + private static final String LAMBDA_INVOKE_OPERATION = "Invoke"; + // Special DEPENDENCY attribute value if GRAPHQL_OPERATION_TYPE attribute key is present. private static final String GRAPHQL = "graphql"; @@ -167,6 +174,7 @@ private Attributes generateDependencyMetricAttributes(SpanData span, Resource re setService(resource, span, builder); setEgressOperation(span, builder); setRemoteServiceAndOperation(span, builder); + setRemoteEnvironment(span, builder); setRemoteResourceTypeAndIdentifier(span, builder); setSpanKindForDependency(span, builder); setHttpStatus(span, builder); @@ -291,6 +299,27 @@ private static void setRemoteServiceAndOperation(SpanData span, AttributesBuilde builder.put(AWS_REMOTE_OPERATION, remoteOperation); } + /** + * Remote environment is used to identify the environment of downstream services. Currently only + * set to "lambda:default" for Lambda Invoke operations when aws-api system is detected. + */ + private static void setRemoteEnvironment(SpanData span, AttributesBuilder builder) { + // We want to treat downstream Lambdas as a service rather than a resource because + // Application Signals topology map gets disconnected due to conflicting Lambda Entity + // definitions + // Additional context can be found in + // https://github.com/aws-observability/aws-otel-python-instrumentation/pull/319 + if (isLambdaInvokeOperation(span)) { + // TODO: This should be passed via ConfigProperties from + // AwsApplicationSignalsCustomizerProvider + String remoteEnvironment = + Optional.ofNullable(System.getenv(LAMBDA_APPLICATION_SIGNALS_REMOTE_ENVIRONMENT)) + .filter(s -> !s.isEmpty()) + .orElse("default"); + builder.put(AWS_REMOTE_ENVIRONMENT, "lambda:" + remoteEnvironment); + } + } + /** * When the remote call operation is undetermined for http use cases, will try to extract the * remote operation name from http url string @@ -373,6 +402,15 @@ private static String generateRemoteService(SpanData span) { return remoteService; } + private static boolean isLambdaInvokeOperation(SpanData span) { + if (!isAwsSDKSpan(span)) { + return false; + } + String rpcService = getRemoteService(span, RPC_SERVICE); + return ("AWSLambda".equals(rpcService) || "Lambda".equals(rpcService)) + && LAMBDA_INVOKE_OPERATION.equals(span.getAttributes().get(RPC_METHOD)); + } + /** * If the span is an AWS SDK span, normalize the name to align with AWS @@ -420,7 +458,19 @@ private static String normalizeRemoteServiceName(SpanData span, String serviceNa return NORMALIZED_SECRETSMANAGER_SERVICE_NAME; case "AWSLambda": // AWS SDK v1 case "Lambda": // AWS SDK v2 - return NORMALIZED_LAMBDA_SERVICE_NAME; + if (isLambdaInvokeOperation(span)) { + // AWS_LAMBDA_NAME can contain either a function name or function ARN since Lambda AWS + // SDK calls accept both formats + Optional lambdaFunctionName = + getLambdaFunctionNameFromArn( + Optional.ofNullable(span.getAttributes().get(AWS_LAMBDA_NAME))); + // If Lambda name is not present, use UnknownRemoteService + // This is intentional - we want to clearly indicate when the Lambda function name + // is missing rather than falling back to a generic service name + return lambdaFunctionName.orElse(UNKNOWN_REMOTE_SERVICE); + } else { + return NORMALIZED_LAMBDA_SERVICE_NAME; + } default: return "AWS::" + serviceName; } @@ -518,6 +568,19 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_SECRET_ARN)))); cloudformationPrimaryIdentifier = Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_SECRET_ARN))); + } else if (isKeyPresent(span, AWS_LAMBDA_NAME)) { + // For non-Invoke Lambda operations, treat Lambda as a resource, + // see normalizeRemoteServiceName for more information. + if (!isLambdaInvokeOperation(span)) { + remoteResourceType = Optional.of(NORMALIZED_LAMBDA_SERVICE_NAME + "::Function"); + // AWS_LAMBDA_NAME can contain either a function name or function ARN since Lambda AWS SDK + // calls accept both formats + remoteResourceIdentifier = + getLambdaFunctionNameFromArn( + Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_LAMBDA_NAME)))); + cloudformationPrimaryIdentifier = + Optional.ofNullable(escapeDelimiters(span.getAttributes().get(AWS_LAMBDA_ARN))); + } } else if (isKeyPresent(span, AWS_LAMBDA_RESOURCE_ID)) { remoteResourceType = Optional.of(NORMALIZED_LAMBDA_SERVICE_NAME + "::EventSourceMapping"); remoteResourceIdentifier = @@ -539,6 +602,14 @@ private static void setRemoteResourceTypeAndIdentifier(SpanData span, Attributes } } + private static Optional getLambdaFunctionNameFromArn(Optional stringArn) { + if (stringArn.isPresent() && stringArn.get().startsWith("arn:aws:lambda:")) { + Arn resourceArn = Arn.fromString(stringArn.get()); + return Optional.of(resourceArn.getResource().toString().split(":")[1]); + } + return stringArn; + } + private static Optional getSecretsManagerResourceNameFromArn(Optional stringArn) { Arn resourceArn = Arn.fromString(stringArn.get()); return Optional.of(resourceArn.getResource().toString().split(":")[1]); 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 bceb8e3111..2811bec965 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 @@ -23,15 +23,20 @@ import static org.mockito.Mockito.when; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_AGENT_ID; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_BUCKET_NAME; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_DATA_SOURCE_ID; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ARN; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_GUARDRAIL_ID; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_KNOWLEDGE_BASE_ID; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_ARN; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LAMBDA_RESOURCE_ID; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_OPERATION; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_LOCAL_SERVICE; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_NAME; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_QUEUE_URL; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_DB_USER; +import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_ENVIRONMENT; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_OPERATION; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_IDENTIFIER; import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_RESOURCE_TYPE; @@ -832,12 +837,29 @@ public void testSdkClientSpanWithRemoteResourceAttributes() { // Validate behaviour of AWS_GUARDRAIL_ID attribute, then remove it. mockAttribute(AWS_GUARDRAIL_ID, "test_guardrail_id"); validateRemoteResourceAttributes("AWS::Bedrock::Guardrail", "test_guardrail_id"); + // Also test with ARN to verify cloudformationPrimaryIdentifier uses ARN + mockAttribute( + AWS_GUARDRAIL_ARN, "arn:aws:bedrock:us-east-1:123456789012:guardrail/test_guardrail_id"); + validateRemoteResourceAttributes( + "AWS::Bedrock::Guardrail", + "test_guardrail_id", + "arn:aws:bedrock:us-east-1:123456789012:guardrail/test_guardrail_id"); mockAttribute(AWS_GUARDRAIL_ID, null); + mockAttribute(AWS_GUARDRAIL_ARN, null); // Validate behaviour of AWS_GUARDRAIL_ID attribute with special chars(^), then remove it. mockAttribute(AWS_GUARDRAIL_ID, "test_guardrail_^id"); validateRemoteResourceAttributes("AWS::Bedrock::Guardrail", "test_guardrail_^^id"); + // Also test with ARN containing special chars to verify delimiter escaping in + // cloudformationPrimaryIdentifier + mockAttribute( + AWS_GUARDRAIL_ARN, "arn:aws:bedrock:us-east-1:123456789012:guardrail/test_guardrail_^id"); + validateRemoteResourceAttributes( + "AWS::Bedrock::Guardrail", + "test_guardrail_^^id", + "arn:aws:bedrock:us-east-1:123456789012:guardrail/test_guardrail_^^id"); mockAttribute(AWS_GUARDRAIL_ID, null); + mockAttribute(AWS_GUARDRAIL_ARN, null); // Validate behaviour of GEN_AI_REQUEST_MODEL attribute, then remove it. mockAttribute(GEN_AI_REQUEST_MODEL, "test.service_id"); @@ -854,27 +876,83 @@ public void testSdkClientSpanWithRemoteResourceAttributes() { mockAttribute( AWS_STATE_MACHINE_ARN, "arn:aws:states:us-east-1:123456789012:stateMachine:test_state_machine"); - validateRemoteResourceAttributes("AWS::StepFunctions::StateMachine", "test_state_machine"); + validateRemoteResourceAttributes( + "AWS::StepFunctions::StateMachine", + "test_state_machine", + "arn:aws:states:us-east-1:123456789012:stateMachine:test_state_machine"); mockAttribute(AWS_STATE_MACHINE_ARN, null); // Validate behaviour of AWS_STEPFUNCTIONS_ACTIVITY_ARN, then remove it. mockAttribute( AWS_STEP_FUNCTIONS_ACTIVITY_ARN, "arn:aws:states:us-east-1:007003123456789012:activity:testActivity"); - validateRemoteResourceAttributes("AWS::StepFunctions::Activity", "testActivity"); + validateRemoteResourceAttributes( + "AWS::StepFunctions::Activity", + "testActivity", + "arn:aws:states:us-east-1:007003123456789012:activity:testActivity"); mockAttribute(AWS_STEP_FUNCTIONS_ACTIVITY_ARN, null); // Validate behaviour of AWS_SNS_TOPIC_ARN, then remove it. mockAttribute(AWS_SNS_TOPIC_ARN, "arn:aws:sns:us-west-2:012345678901:testTopic"); - validateRemoteResourceAttributes("AWS::SNS::Topic", "testTopic"); + validateRemoteResourceAttributes( + "AWS::SNS::Topic", "testTopic", "arn:aws:sns:us-west-2:012345678901:testTopic"); mockAttribute(AWS_SNS_TOPIC_ARN, null); // Validate behaviour of AWS_SECRET_ARN, then remove it. mockAttribute( AWS_SECRET_ARN, "arn:aws:secretsmanager:us-east-1:123456789012:secret:secretName"); - validateRemoteResourceAttributes("AWS::SecretsManager::Secret", "secretName"); + validateRemoteResourceAttributes( + "AWS::SecretsManager::Secret", + "secretName", + "arn:aws:secretsmanager:us-east-1:123456789012:secret:secretName"); mockAttribute(AWS_SECRET_ARN, null); + // Validate behaviour of AWS_LAMBDA_NAME for non-Invoke operations (treated as resource) + mockAttribute(RPC_SERVICE, "Lambda"); + mockAttribute(RPC_METHOD, "GetFunction"); + mockAttribute(AWS_LAMBDA_NAME, "testLambdaName"); + mockAttribute(AWS_LAMBDA_ARN, "arn:aws:lambda:us-east-1:123456789012:function:testLambdaName"); + validateRemoteResourceAttributes( + "AWS::Lambda::Function", + "testLambdaName", + "arn:aws:lambda:us-east-1:123456789012:function:testLambdaName"); + mockAttribute(RPC_SERVICE, null); + mockAttribute(RPC_METHOD, null); + mockAttribute(AWS_LAMBDA_NAME, null); + mockAttribute(AWS_LAMBDA_ARN, null); + + // Validate behaviour of AWS_LAMBDA_NAME containing ARN for non-Invoke operations + mockAttribute(RPC_SERVICE, "Lambda"); + mockAttribute(RPC_METHOD, "ListFunctions"); + mockAttribute(AWS_LAMBDA_NAME, "arn:aws:lambda:us-east-1:123456789012:function:testLambdaName"); + mockAttribute(AWS_LAMBDA_ARN, "arn:aws:lambda:us-east-1:123456789012:function:testLambdaName"); + validateRemoteResourceAttributes( + "AWS::Lambda::Function", + "testLambdaName", + "arn:aws:lambda:us-east-1:123456789012:function:testLambdaName"); + mockAttribute(RPC_SERVICE, null); + mockAttribute(RPC_METHOD, null); + mockAttribute(AWS_LAMBDA_NAME, null); + mockAttribute(AWS_LAMBDA_ARN, null); + + // Validate that Lambda Invoke with function name treats Lambda as a service, not a resource + mockAttribute(RPC_SERVICE, "Lambda"); + mockAttribute(RPC_METHOD, "Invoke"); + mockAttribute(AWS_LAMBDA_NAME, "testLambdaName"); + validateRemoteResourceAttributes(null, null); + mockAttribute(RPC_SERVICE, null); + mockAttribute(RPC_METHOD, null); + mockAttribute(AWS_LAMBDA_NAME, null); + + // Validate behaviour of AWS_LAMBDA_NAME containing ARN for Invoke operations + mockAttribute(RPC_SERVICE, "Lambda"); + mockAttribute(RPC_METHOD, "Invoke"); + mockAttribute(AWS_LAMBDA_NAME, "arn:aws:lambda:us-east-1:123456789012:function:testLambdaName"); + validateRemoteResourceAttributes(null, null); + mockAttribute(RPC_SERVICE, null); + mockAttribute(RPC_METHOD, null); + mockAttribute(AWS_LAMBDA_NAME, null); + // Validate behaviour of AWS_LAMBDA_RESOURCE_ID mockAttribute(AWS_LAMBDA_RESOURCE_ID, "eventSourceId"); validateRemoteResourceAttributes("AWS::Lambda::EventSourceMapping", "eventSourceId"); @@ -883,6 +961,64 @@ public void testSdkClientSpanWithRemoteResourceAttributes() { mockAttribute(RPC_SYSTEM, "null"); } + @Test + public void testCloudFormationPrimaryIdentifierFallbackToRemoteResourceIdentifier() { + // Test that when cloudformationPrimaryIdentifier is not explicitly set, + // it falls back to use the same value as remoteResourceIdentifier + + mockAttribute(RPC_SYSTEM, "aws-api"); + when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT); + + // Test case 1: S3 Bucket (no ARN available, should use bucket name for both) + mockAttribute(AWS_BUCKET_NAME, "my-test-bucket"); + validateRemoteResourceAttributes("AWS::S3::Bucket", "my-test-bucket"); + + // Test S3 bucket with special characters + mockAttribute(AWS_BUCKET_NAME, "my-test|bucket^name"); + validateRemoteResourceAttributes("AWS::S3::Bucket", "my-test^|bucket^^name"); + mockAttribute(AWS_BUCKET_NAME, null); + + // Test case 2: SQS Queue by name (no ARN, should use queue name for both) + mockAttribute(AWS_QUEUE_NAME, "my-test-queue"); + validateRemoteResourceAttributes("AWS::SQS::Queue", "my-test-queue"); + + // Test SQS queue with special characters + mockAttribute(AWS_QUEUE_NAME, "my^queue|name"); + validateRemoteResourceAttributes("AWS::SQS::Queue", "my^^queue^|name"); + mockAttribute(AWS_QUEUE_NAME, null); + + // Test case 3: DynamoDB Table (no ARN, should use table name for both) + mockAttribute(AWS_TABLE_NAME, "my-test-table"); + validateRemoteResourceAttributes("AWS::DynamoDB::Table", "my-test-table"); + + // Test DynamoDB table with special characters + mockAttribute(AWS_TABLE_NAME, "my|test^table"); + validateRemoteResourceAttributes("AWS::DynamoDB::Table", "my^|test^^table"); + mockAttribute(AWS_TABLE_NAME, null); + + // Test case 4: Kinesis Stream + mockAttribute(AWS_STREAM_NAME, "my-test-stream"); + validateRemoteResourceAttributes("AWS::Kinesis::Stream", "my-test-stream"); + + // Test Kinesis stream with special characters + mockAttribute(AWS_STREAM_NAME, "my-stream^with|chars"); + validateRemoteResourceAttributes("AWS::Kinesis::Stream", "my-stream^^with^|chars"); + mockAttribute(AWS_STREAM_NAME, null); + + // Test case 5: Lambda Function (non-invoke operation, no ARN) + mockAttribute(RPC_METHOD, "GetFunction"); // Non-invoke operation + mockAttribute(AWS_LAMBDA_NAME, "my-test-function"); + validateRemoteResourceAttributes("AWS::Lambda::Function", "my-test-function"); + + // Test Lambda function with special characters + mockAttribute(AWS_LAMBDA_NAME, "my-function|with^chars"); + validateRemoteResourceAttributes("AWS::Lambda::Function", "my-function^|with^^chars"); + mockAttribute(AWS_LAMBDA_NAME, null); + mockAttribute(RPC_METHOD, null); + + mockAttribute(RPC_SYSTEM, null); + } + @Test public void testDBClientSpanWithRemoteResourceAttributes() { mockAttribute(DB_SYSTEM, "mysql"); @@ -1149,24 +1285,35 @@ private void validatePeerServiceDoesOverride(AttributeKey remoteServiceK } private void validateRemoteResourceAttributes(String type, String identifier) { + validateRemoteResourceAttributes(type, identifier, identifier); + } + + private void validateRemoteResourceAttributes( + String type, String identifier, String cloudformationPrimaryIdentifier) { // Client, Producer and Consumer spans should generate the expected remote resource attributes when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT); Attributes actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(type); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(identifier); + assertThat(actualAttributes.get(AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER)) + .isEqualTo(cloudformationPrimaryIdentifier); when(spanDataMock.getKind()).thenReturn(SpanKind.PRODUCER); actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(type); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(identifier); + assertThat(actualAttributes.get(AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER)) + .isEqualTo(cloudformationPrimaryIdentifier); when(spanDataMock.getKind()).thenReturn(SpanKind.CONSUMER); actualAttributes = GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(type); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(identifier); + assertThat(actualAttributes.get(AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER)) + .isEqualTo(cloudformationPrimaryIdentifier); // Server span should not generate remote resource attributes when(spanDataMock.getKind()).thenReturn(SpanKind.SERVER); @@ -1174,6 +1321,7 @@ private void validateRemoteResourceAttributes(String type, String identifier) { GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(SERVICE_METRIC); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_TYPE)).isEqualTo(null); assertThat(actualAttributes.get(AWS_REMOTE_RESOURCE_IDENTIFIER)).isEqualTo(null); + assertThat(actualAttributes.get(AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER)).isEqualTo(null); } private void validateHttpStatusWithThrowable(Throwable throwable, Long expectedStatusCode) { @@ -1287,6 +1435,25 @@ public void testNormalizeRemoteServiceName_AwsSdk() { testAwsSdkServiceNormalization("AmazonBedrockRuntime", "AWS::BedrockRuntime"); testAwsSdkServiceNormalization("AWSStepFunctions", "AWS::StepFunctions"); + // AWS SDK V1 Lambda tests + testAwsSdkServiceNormalization("Lambda", "AWS::Lambda"); + mockAttribute(RPC_METHOD, "Invoke"); + mockAttribute(AWS_LAMBDA_NAME, "testFunction"); + testAwsSdkServiceNormalization("Lambda", "testFunction"); + // Test Lambda Invoke without AWS_LAMBDA_NAME - should fall back to UnknownRemoteService + mockAttribute(AWS_LAMBDA_NAME, null); + testAwsSdkServiceNormalization("Lambda", "UnknownRemoteService"); + mockAttribute(RPC_METHOD, null); + + testAwsSdkServiceNormalization("AWSLambda", "AWS::Lambda"); + mockAttribute(RPC_METHOD, "Invoke"); + mockAttribute(AWS_LAMBDA_NAME, "testFunction"); + testAwsSdkServiceNormalization("AWSLambda", "testFunction"); + // Test Lambda Invoke without AWS_LAMBDA_NAME - should fall back to UnknownRemoteService + mockAttribute(AWS_LAMBDA_NAME, null); + testAwsSdkServiceNormalization("AWSLambda", "UnknownRemoteService"); + mockAttribute(RPC_METHOD, null); + // AWS SDK V2 testAwsSdkServiceNormalization("DynamoDb", "AWS::DynamoDB"); testAwsSdkServiceNormalization("Kinesis", "AWS::Kinesis"); @@ -1308,6 +1475,55 @@ private void testAwsSdkServiceNormalization(String serviceName, String expectedR assertThat(actualAttributes.get(AWS_REMOTE_SERVICE)).isEqualTo(expectedRemoteService); } + @Test + public void testSetRemoteEnvironment() { + // Test 1: Setting remote environment when all relevant attributes are present + mockAttribute(RPC_SYSTEM, "aws-api"); + mockAttribute(RPC_SERVICE, "Lambda"); + mockAttribute(RPC_METHOD, "Invoke"); + mockAttribute(AWS_LAMBDA_NAME, "testFunction"); + when(spanDataMock.getKind()).thenReturn(SpanKind.CLIENT); + + Attributes actualAttributes = + GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); + assertThat(actualAttributes.get(AWS_REMOTE_ENVIRONMENT)).isEqualTo("lambda:default"); + + // Test 2: NOT setting it when RPC_SYSTEM is missing + mockAttribute(RPC_SYSTEM, null); + actualAttributes = + GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); + assertThat(actualAttributes.get(AWS_REMOTE_ENVIRONMENT)).isNull(); + mockAttribute(RPC_SYSTEM, "aws-api"); + + // Test 3: NOT setting it when RPC_METHOD is missing + mockAttribute(RPC_METHOD, null); + actualAttributes = + GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); + assertThat(actualAttributes.get(AWS_REMOTE_ENVIRONMENT)).isNull(); + mockAttribute(RPC_METHOD, "Invoke"); + + // Test 4: Still setting it to lambda:default when AWS_LAMBDA_NAME is missing + mockAttribute(AWS_LAMBDA_NAME, null); + actualAttributes = + GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); + assertThat(actualAttributes.get(AWS_REMOTE_ENVIRONMENT)).isEqualTo("lambda:default"); + mockAttribute(AWS_LAMBDA_NAME, "testFunction"); + + // Test 5: NOT setting it for non-Lambda services + mockAttribute(RPC_SERVICE, "S3"); + mockAttribute(RPC_METHOD, "GetObject"); + actualAttributes = + GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); + assertThat(actualAttributes.get(AWS_REMOTE_ENVIRONMENT)).isNull(); + + // Test 6: NOT setting it for Lambda non-Invoke operations + mockAttribute(RPC_SERVICE, "Lambda"); + mockAttribute(RPC_METHOD, "GetFunction"); + actualAttributes = + GENERATOR.generateMetricAttributeMapFromSpan(spanDataMock, resource).get(DEPENDENCY_METRIC); + assertThat(actualAttributes.get(AWS_REMOTE_ENVIRONMENT)).isNull(); + } + @Test public void testNoMetricWhenConsumerProcessWithConsumerParent() { when(attributesMock.get(AwsAttributeKeys.AWS_CONSUMER_PARENT_SPAN_KIND)) diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts index af8f793dc7..00aa199d82 100644 --- a/dependencyManagement/build.gradle.kts +++ b/dependencyManagement/build.gradle.kts @@ -27,8 +27,8 @@ data class DependencySet(val group: String, val version: String, val modules: Li val testSnapshots = rootProject.findProperty("testUpstreamSnapshots") == "true" // This is the version of the upstream instrumentation BOM -val otelVersion = "2.11.0-adot1" -val otelSnapshotVersion = "2.11.0" +val otelVersion = "2.11.0-adot2" +val otelSnapshotVersion = "2.12.0" val otelAlphaVersion = if (!testSnapshots) "$otelVersion-alpha" else "$otelSnapshotVersion-alpha-SNAPSHOT" val otelJavaAgentVersion = if (!testSnapshots) otelVersion else "$otelSnapshotVersion-SNAPSHOT" // All versions below are only used in testing and do not affect the released artifact. @@ -73,7 +73,7 @@ val dependencyLists = listOf( "commons-logging:commons-logging:1.2", "com.sparkjava:spark-core:2.9.4", "com.squareup.okhttp3:okhttp:4.12.0", - "io.opentelemetry.contrib:opentelemetry-aws-xray:1.39.0", + "io.opentelemetry.contrib:opentelemetry-aws-xray:1.39.0-adot1", "io.opentelemetry.contrib:opentelemetry-aws-resources:1.39.0-alpha", "io.opentelemetry.proto:opentelemetry-proto:1.0.0-alpha", "io.opentelemetry.javaagent:opentelemetry-javaagent:$otelJavaAgentVersion", diff --git a/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/CHANGELOG.md b/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/CHANGELOG.md index 6338d21f93..d22a507f51 100644 --- a/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/CHANGELOG.md +++ b/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/CHANGELOG.md @@ -1 +1,5 @@ # Release History: aws-distro-opentelemetry-xray-udp-span-exporter + +### v1.0.0 / 2025-05-15 + +* Initial release diff --git a/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/build.gradle.kts b/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/build.gradle.kts index 4ab496c135..9aec0ccbed 100644 --- a/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/build.gradle.kts +++ b/exporters/aws-distro-opentelemetry-xray-udp-span-exporter/build.gradle.kts @@ -23,7 +23,7 @@ plugins { } group = "software.amazon.distro.opentelemetry" -version = "0.1.0" +version = "1.0.0" dependencies { implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:2.10.0")) diff --git a/lambda-layer/build-layer.sh b/lambda-layer/build-layer.sh index e694e59aab..36350cd5b1 100755 --- a/lambda-layer/build-layer.sh +++ b/lambda-layer/build-layer.sh @@ -34,6 +34,22 @@ patch -p1 < "$SOURCEDIR"/patches/StreamHandlerInstrumentation.patch popd rm -rf opentelemetry-java-instrumentation +contrib_version=$(awk -F'=v' '/OTEL_JAVA_CONTRIB_VERSION/ {print $2}' "$file") +if [[ -n "$contrib_version" ]]; then + echo "Found OTEL Contrib Version: ${contrib_version}" + ## Clone and Patch the OpenTelemetry Java contrib Repository + echo "Info: Cloning and Patching OpenTelemetry Java contrib Repository" + git clone https://github.com/open-telemetry/opentelemetry-java-contrib.git + pushd opentelemetry-java-contrib + git checkout v${contrib_version} -b tag-v${contrib_version} + + # There is another patch in the .github/patches directory for other changes. We should apply them too for consistency. + patch -p1 < "$SOURCEDIR"/../.github/patches/opentelemetry-java-contrib.patch + + ./gradlew publishToMavenLocal + popd + rm -rf opentelemetry-java-contrib +fi ## Build the ADOT Java from current source echo "Info: Building ADOT Java from current source" diff --git a/lambda-layer/patches/aws-otel-java-instrumentation.patch b/lambda-layer/patches/aws-otel-java-instrumentation.patch index beaa02d23e..99dd328af1 100644 --- a/lambda-layer/patches/aws-otel-java-instrumentation.patch +++ b/lambda-layer/patches/aws-otel-java-instrumentation.patch @@ -6,8 +6,8 @@ index 9493189..6090207 100644 val TEST_SNAPSHOTS = rootProject.findProperty("testUpstreamSnapshots") == "true" // This is the version of the upstream instrumentation BOM --val otelVersion = "2.11.0-adot1" +-val otelVersion = "2.11.0-adot2" +val otelVersion = "2.11.0-adot-lambda1" - val otelSnapshotVersion = "2.11.0" + val otelSnapshotVersion = "2.12.0" val otelAlphaVersion = if (!TEST_SNAPSHOTS) "$otelVersion-alpha" else "$otelSnapshotVersion-alpha-SNAPSHOT" val otelJavaAgentVersion = if (!TEST_SNAPSHOTS) otelVersion else "$otelSnapshotVersion-SNAPSHOT" diff --git a/lambda-layer/patches/opentelemetry-java-instrumentation.patch b/lambda-layer/patches/opentelemetry-java-instrumentation.patch index cca35f0ed0..e9253c5d87 100644 --- a/lambda-layer/patches/opentelemetry-java-instrumentation.patch +++ b/lambda-layer/patches/opentelemetry-java-instrumentation.patch @@ -310,8 +310,8 @@ index 7900c9a4d9..80383d7c22 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -1,5 +1,5 @@ --val stableVersion = "2.11.0-adot1" --val alphaVersion = "2.11.0-adot1-alpha" +-val stableVersion = "2.11.0-adot2" +-val alphaVersion = "2.11.0-adot2-alpha" +val stableVersion = "2.11.0-adot-lambda1" +val alphaVersion = "2.11.0-adot-lambda1-alpha"