Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge main to v2.0.x #1031

Closed
wants to merge 10 commits into from
13 changes: 13 additions & 0 deletions .github/workflows/application-signals-e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,19 @@ jobs:
java-version: '11'
cpu-architecture: 'arm64'

#
# UBUNTU COVERAGE
# DEFAULT SETTING: Java 11, EC2, AMD64, Ubuntu
#

v11-amd64-ubuntu:
needs: [ upload-main-build ]
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/java-ec2-ubuntu-test.yml@main
secrets: inherit
with:
aws-region: us-east-1
caller-workflow-name: 'main-build'

#
# Other Functional Test Case
#
Expand Down
218 changes: 218 additions & 0 deletions .github/workflows/release-lambda.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
name: Release Java Lambda layer

on:
workflow_dispatch:
inputs:
version:
description: The version to tag the lambda release with, e.g., 1.2.0
required: true
aws_region:
description: 'Deploy to aws regions'
required: true
default: 'us-east-1, us-east-2, us-west-1, us-west-2, ap-south-1, ap-northeast-3, ap-northeast-2, ap-southeast-1, ap-southeast-2, ap-northeast-1, ca-central-1, eu-central-1, eu-west-1, eu-west-2, eu-west-3, eu-north-1, sa-east-1, af-south-1, ap-east-1, ap-south-2, ap-southeast-3, ap-southeast-4, eu-central-2, eu-south-1, eu-south-2, il-central-1, me-central-1, me-south-1'

env:
COMMERCIAL_REGIONS: us-east-1, us-east-2, us-west-1, us-west-2, ap-south-1, ap-northeast-3, ap-northeast-2, ap-southeast-1, ap-southeast-2, ap-northeast-1, ca-central-1, eu-central-1, eu-west-1, eu-west-2, eu-west-3, eu-north-1, sa-east-1
LAYER_NAME: AWSOpenTelemetryDistroJava

permissions:
id-token: write
contents: write

jobs:
build-layer:
runs-on: ubuntu-latest
outputs:
aws_regions_json: ${{ steps.set-matrix.outputs.aws_regions_json }}
steps:
- name: Set up regions matrix
id: set-matrix
run: |
IFS=',' read -ra REGIONS <<< "${{ github.event.inputs.aws_region }}"
MATRIX="["
for region in "${REGIONS[@]}"; do
trimmed_region=$(echo "$region" | xargs)
MATRIX+="\"$trimmed_region\","
done
MATRIX="${MATRIX%,}]"
echo ${MATRIX}
echo "aws_regions_json=${MATRIX}" >> $GITHUB_OUTPUT

- name: Checkout Repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
java-version: 17
distribution: 'temurin'

- name: Build layers
working-directory: lambda-layer
run: |
./build-layer.sh

- name: Upload layer
uses: actions/upload-artifact@v4
with:
name: aws-opentelemetry-java-layer.zip
path: lambda-layer/build/distributions/aws-opentelemetry-java-layer.zip

publish-prod:
runs-on: ubuntu-latest
needs: build-layer
strategy:
matrix:
aws_region: ${{ fromJson(needs.build-layer.outputs.aws_regions_json) }}
steps:
- name: role arn
env:
COMMERCIAL_REGIONS: ${{ env.COMMERCIAL_REGIONS }}
run: |
COMMERCIAL_REGIONS_ARRAY=(${COMMERCIAL_REGIONS//,/ })
FOUND=false
for REGION in "${COMMERCIAL_REGIONS_ARRAY[@]}"; do
if [[ "$REGION" == "${{ matrix.aws_region }}" ]]; then
FOUND=true
break
fi
done
if [ "$FOUND" = true ]; then
echo "Found ${{ matrix.aws_region }} in COMMERCIAL_REGIONS"
SECRET_KEY="LAMBDA_LAYER_RELEASE"
else
echo "Not found ${{ matrix.aws_region }} in COMMERCIAL_REGIONS"
SECRET_KEY="${{ matrix.aws_region }}_LAMBDA_LAYER_RELEASE"
fi
SECRET_KEY=${SECRET_KEY//-/_}
echo "SECRET_KEY=${SECRET_KEY}" >> $GITHUB_ENV

- uses: aws-actions/[email protected]
with:
role-to-assume: ${{ secrets[env.SECRET_KEY] }}
role-duration-seconds: 1200
aws-region: ${{ matrix.aws_region }}

- name: Get s3 bucket name for release
run: |
echo BUCKET_NAME=java-lambda-layer-${{ github.run_id }}-${{ matrix.aws_region }} | tee --append $GITHUB_ENV

- name: download layer.zip
uses: actions/download-artifact@v4
with:
name: aws-opentelemetry-java-layer.zip

- name: publish
run: |
aws s3 mb s3://${{ env.BUCKET_NAME }}
aws s3 cp aws-opentelemetry-java-layer.zip s3://${{ env.BUCKET_NAME }}
layerARN=$(
aws lambda publish-layer-version \
--layer-name ${{ env.LAYER_NAME }} \
--content S3Bucket=${{ env.BUCKET_NAME }},S3Key=aws-opentelemetry-java-layer.zip \
--compatible-runtimes java17 java21 \
--compatible-architectures "arm64" "x86_64" \
--license-info "Apache-2.0" \
--description "AWS Distro of OpenTelemetry Lambda Layer for Java Runtime" \
--query 'LayerVersionArn' \
--output text
)
echo $layerARN
echo "LAYER_ARN=${layerARN}" >> $GITHUB_ENV
mkdir ${{ env.LAYER_NAME }}
echo $layerARN > ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}
cat ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}

- name: public layer
run: |
layerVersion=$(
aws lambda list-layer-versions \
--layer-name ${{ env.LAYER_NAME }} \
--query 'max_by(LayerVersions, &Version).Version'
)
aws lambda add-layer-version-permission \
--layer-name ${{ env.LAYER_NAME }} \
--version-number $layerVersion \
--principal "*" \
--statement-id publish \
--action lambda:GetLayerVersion

- name: upload layer arn artifact
if: ${{ success() }}
uses: actions/upload-artifact@v4
with:
name: ${{ env.LAYER_NAME }}
path: ${{ env.LAYER_NAME }}/${{ matrix.aws_region }}

- name: clean s3
if: always()
run: |
aws s3 rb --force s3://${{ env.BUCKET_NAME }}

generate-release-note:
runs-on: ubuntu-latest
needs: publish-prod
steps:
- name: Checkout Repo @ SHA - ${{ github.sha }}
uses: actions/checkout@v4

- uses: hashicorp/setup-terraform@v2

- name: download layerARNs
uses: actions/download-artifact@v4
with:
name: ${{ env.LAYER_NAME }}
path: ${{ env.LAYER_NAME }}

- name: show layerARNs
run: |
for file in ${{ env.LAYER_NAME }}/*
do
echo $file
cat $file
done

- name: generate layer-note
working-directory: ${{ env.LAYER_NAME }}
run: |
echo "| Region | Layer ARN |" >> ../layer-note
echo "| ---- | ---- |" >> ../layer-note
for file in *
do
read arn < $file
echo "| " $file " | " $arn " |" >> ../layer-note
done
cd ..
cat layer-note

- name: generate tf layer
working-directory: ${{ env.LAYER_NAME }}
run: |
echo "locals {" >> ../layer.tf
echo " sdk_layer_arns = {" >> ../layer.tf
for file in *
do
read arn < $file
echo " \""$file"\" = \""$arn"\"" >> ../layer.tf
done
cd ..
echo " }" >> layer.tf
echo "}" >> layer.tf
terraform fmt layer.tf
cat layer.tf

- name: upload layer tf file
uses: actions/upload-artifact@v4
with:
name: layer.tf
path: layer.tf

- name: Create GH release
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # This token is provided by Actions, you do not need to create your own token
run: |
gh release create --target "$GITHUB_REF_NAME" \
--title "Release lambda-v${{ github.event.inputs.version }}" \
--draft \
"lambda-v${{ github.event.inputs.version }}" \
layer.tf
2 changes: 1 addition & 1 deletion appsignals-tests/images/grpc/grpc-base/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protobuf {
}
plugins {
create("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.56.1"
artifact = "io.grpc:protoc-gen-grpc-java:1.69.1"
}
}
generateProtoTasks {
Expand Down
5 changes: 4 additions & 1 deletion awsagentprovider/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ dependencies {
// Import AWS SDK v1 core for ARN parsing utilities
implementation("com.amazonaws:aws-java-sdk-core:1.12.773")
// Export configuration
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
implementation("io.opentelemetry:opentelemetry-exporter-otlp")
// For Udp emitter
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp-common")
// For HTTP SigV4 emitter
implementation("software.amazon.awssdk:auth:2.30.14")
implementation("software.amazon.awssdk:http-auth-aws:2.30.14")

testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
testImplementation("io.opentelemetry:opentelemetry-sdk-testing")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void customize(AutoConfigurationCustomizer autoConfiguration) {
() ->
new HashMap<String, String>() {
{
put("otel.propagators", "xray,tracecontext,b3,b3multi");
put("otel.propagators", "baggage,xray,tracecontext,b3,b3multi");
put("otel.instrumentation.aws-sdk.experimental-span-attributes", "true");
put(
"otel.instrumentation.aws-sdk.experimental-record-individual-http-error",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;

/**
* This customizer performs the following customizations:
Expand All @@ -70,6 +71,8 @@
public class AwsApplicationSignalsCustomizerProvider
implements AutoConfigurationCustomizerProvider {
static final String AWS_LAMBDA_FUNCTION_NAME_CONFIG = "AWS_LAMBDA_FUNCTION_NAME";
private static final String XRAY_OTLP_ENDPOINT_PATTERN =
"^https://xray\\.([a-z0-9-]+)\\.amazonaws\\.com/v1/traces$";

private static final Duration DEFAULT_METRIC_EXPORT_INTERVAL = Duration.ofMinutes(1);
private static final Logger logger =
Expand Down Expand Up @@ -121,6 +124,16 @@ static boolean isLambdaEnvironment() {
return System.getenv(AWS_LAMBDA_FUNCTION_NAME_CONFIG) != null;
}

static boolean isXrayOtlpEndpoint(String otlpEndpoint) {
if (otlpEndpoint == null) {
return false;
}

return Pattern.compile(XRAY_OTLP_ENDPOINT_PATTERN)
.matcher(otlpEndpoint.toLowerCase())
.matches();
}

private boolean isApplicationSignalsEnabled(ConfigProperties configProps) {
return configProps.getBoolean(
APPLICATION_SIGNALS_ENABLED_CONFIG,
Expand Down Expand Up @@ -221,6 +234,10 @@ private SdkTracerProviderBuilder customizeTracerProviderBuilder(
return tracerProviderBuilder;
}

if (isXrayOtlpEndpoint(System.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG))) {
return tracerProviderBuilder;
}

// Construct meterProvider
MetricExporter metricsExporter =
ApplicationSignalsExporterProvider.INSTANCE.createExporter(configProps);
Expand Down Expand Up @@ -286,6 +303,14 @@ private SpanExporter customizeSpanExporter(
.build();
}
}
// When running OTLP endpoint for X-Ray backend, use custom exporter for SigV4 authentication
else if (spanExporter instanceof OtlpHttpSpanExporter
&& isXrayOtlpEndpoint(System.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG))) {
spanExporter =
new OtlpAwsSpanExporter(
(OtlpHttpSpanExporter) spanExporter,
System.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT_CONFIG));
}

if (isApplicationSignalsEnabled(configProps)) {
return AwsMetricAttributesSpanExporterBuilder.create(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static io.opentelemetry.semconv.SemanticAttributes.HTTP_RESPONSE_STATUS_CODE;
import static io.opentelemetry.semconv.SemanticAttributes.HTTP_STATUS_CODE;
import static software.amazon.opentelemetry.javaagent.providers.AwsAttributeKeys.AWS_REMOTE_SERVICE;
import static software.amazon.opentelemetry.javaagent.providers.AwsSpanProcessingUtil.isKeyPresent;

import io.opentelemetry.api.common.Attributes;
Expand Down Expand Up @@ -61,6 +62,10 @@ public final class AwsSpanMetricsProcessor implements SpanProcessor {
private static final int FAULT_CODE_LOWER_BOUND = 500;
private static final int FAULT_CODE_UPPER_BOUND = 599;

// EC2 Metadata API IP Address
// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html#instancedata-inside-access
private final String EC2_METADATA_API_IP = "169.254.169.254";

// Metric instruments
private final LongHistogram errorHistogram;
private final LongHistogram faultHistogram;
Expand Down Expand Up @@ -172,9 +177,18 @@ private void recordLatency(ReadableSpan span, Attributes attributes) {

private void recordMetrics(ReadableSpan span, SpanData spanData, Attributes attributes) {
// Only record metrics if non-empty attributes are returned.
if (!attributes.isEmpty()) {
if (!attributes.isEmpty() && !isEc2MetadataSpan((attributes))) {
recordErrorOrFault(spanData, attributes);
recordLatency(span, attributes);
}
}

private boolean isEc2MetadataSpan(Attributes attributes) {
if (attributes.get(AWS_REMOTE_SERVICE) != null
&& attributes.get(AWS_REMOTE_SERVICE).equals(EC2_METADATA_API_IP)) {
return true;
}

return false;
}
}
Loading
Loading