diff --git a/instrumentation/aws_lambda/CHANGELOG.md b/instrumentation/aws_lambda/CHANGELOG.md index 8a791ca5c8..d7e805cb49 100644 --- a/instrumentation/aws_lambda/CHANGELOG.md +++ b/instrumentation/aws_lambda/CHANGELOG.md @@ -1,5 +1,9 @@ # Release History: opentelemetry-instrumentation-aws_lambda +### Unreleased + +* ADDED: Read `cloud.account.id` from symlink created by the OTel Lambda Extension + ### v0.6.0 / 2025-10-22 * BREAKING CHANGE: Min Ruby Version 3.2 diff --git a/resources/aws/lib/opentelemetry/resource/detector/aws/lambda.rb b/resources/aws/lib/opentelemetry/resource/detector/aws/lambda.rb index f8db75bbfd..0e28d847e8 100644 --- a/resources/aws/lib/opentelemetry/resource/detector/aws/lambda.rb +++ b/resources/aws/lib/opentelemetry/resource/detector/aws/lambda.rb @@ -17,6 +17,9 @@ module Lambda # Create a constant for resource semantic conventions RESOURCE = OpenTelemetry::SemanticConventions::Resource + # Path to the symlink created by the OTel Lambda extension containing the AWS account ID + ACCOUNT_ID_SYMLINK_PATH = '/tmp/.otel-account-id' + def detect # Return empty resource if not running on Lambda return OpenTelemetry::SDK::Resources::Resource.create({}) unless lambda_environment? @@ -34,6 +37,14 @@ def detect # Convert memory size to integer resource_attributes[RESOURCE::FAAS_MAX_MEMORY] = ENV['AWS_LAMBDA_FUNCTION_MEMORY_SIZE'].to_i if ENV['AWS_LAMBDA_FUNCTION_MEMORY_SIZE'] + + # Read cloud.account.id from symlink created by the OTel Lambda extension + begin + account_id = File.readlink(ACCOUNT_ID_SYMLINK_PATH) + resource_attributes[RESOURCE::CLOUD_ACCOUNT_ID] = account_id + rescue Errno::ENOENT, Errno::EINVAL + # Symlink doesn't exist or is not a symlink — silently skip + end rescue StandardError => e OpenTelemetry.handle_error(exception: e, message: 'Lambda resource detection failed') return OpenTelemetry::SDK::Resources::Resource.create({}) diff --git a/resources/aws/test/opentelemetry/resource/detector/aws/lambda_test.rb b/resources/aws/test/opentelemetry/resource/detector/aws/lambda_test.rb index c94fdcad21..cd8536db32 100644 --- a/resources/aws/test/opentelemetry/resource/detector/aws/lambda_test.rb +++ b/resources/aws/test/opentelemetry/resource/detector/aws/lambda_test.rb @@ -61,6 +61,40 @@ _(attributes).wont_include(OpenTelemetry::SemanticConventions::Resource::FAAS_MAX_MEMORY) end + + describe 'cloud.account.id from symlink' do + it 'reads cloud.account.id from the symlink' do + File.stub :readlink, '123456789012' do + resource = detector.detect + attributes = resource.attribute_enumerator.to_h + _(attributes[OpenTelemetry::SemanticConventions::Resource::CLOUD_ACCOUNT_ID]).must_equal('123456789012') + end + end + + it 'preserves leading zeros in account id' do + File.stub :readlink, '000123456789' do + resource = detector.detect + attributes = resource.attribute_enumerator.to_h + _(attributes[OpenTelemetry::SemanticConventions::Resource::CLOUD_ACCOUNT_ID]).must_equal('000123456789') + end + end + + it 'silently skips when symlink does not exist' do + File.stub :readlink, ->(_path) { raise Errno::ENOENT } do + resource = detector.detect + attributes = resource.attribute_enumerator.to_h + _(attributes).wont_include(OpenTelemetry::SemanticConventions::Resource::CLOUD_ACCOUNT_ID) + end + end + + it 'silently skips when path is not a symlink' do + File.stub :readlink, ->(_path) { raise Errno::EINVAL } do + resource = detector.detect + attributes = resource.attribute_enumerator.to_h + _(attributes).wont_include(OpenTelemetry::SemanticConventions::Resource::CLOUD_ACCOUNT_ID) + end + end + end end describe 'when partial Lambda environment is detected' do