From 1f602e98abbf43c21a81fe158c9042eb1bf01102 Mon Sep 17 00:00:00 2001 From: Gary Sassano <10464497+garysassano@users.noreply.github.com> Date: Fri, 17 Jan 2025 14:39:51 +0100 Subject: [PATCH] feat(detector-aws): add attributes to aws lambda detector --- .../src/detectors/AwsLambdaDetectorSync.ts | 49 +++++++++----- .../test/detectors/AwsLambdaDetector.test.ts | 1 + .../detectors/AwsLambdaDetectorSync.test.ts | 64 +++++++++++++++---- 3 files changed, 86 insertions(+), 28 deletions(-) diff --git a/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsLambdaDetectorSync.ts b/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsLambdaDetectorSync.ts index da7187e565..64bae2e4a9 100644 --- a/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsLambdaDetectorSync.ts +++ b/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsLambdaDetectorSync.ts @@ -22,43 +22,62 @@ import { ResourceDetectionConfig, } from '@opentelemetry/resources'; import { + // AWS attributes + ATTR_AWS_LOG_GROUP_NAMES, + // Cloud attributes ATTR_CLOUD_PROVIDER, ATTR_CLOUD_PLATFORM, ATTR_CLOUD_REGION, - ATTR_FAAS_VERSION, - ATTR_FAAS_NAME, + // Cloud values (AWS) CLOUD_PROVIDER_VALUE_AWS, CLOUD_PLATFORM_VALUE_AWS_LAMBDA, + // FaaS attributes + ATTR_FAAS_NAME, + ATTR_FAAS_VERSION, + ATTR_FAAS_INSTANCE, + ATTR_FAAS_MAX_MEMORY, } from '@opentelemetry/semantic-conventions/incubating'; /** * The AwsLambdaDetector can be used to detect if a process is running in AWS Lambda * and return a {@link Resource} populated with data about the environment. * Returns an empty Resource if detection fails. + * + * AWS Lambda documentation for available environment variables within the Lambda runtimes. + * @see https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime */ export class AwsLambdaDetectorSync implements DetectorSync { detect(_config?: ResourceDetectionConfig): IResource { - const functionName = process.env.AWS_LAMBDA_FUNCTION_NAME; - if (!functionName) { + // Check if running inside AWS Lambda environment + const executionEnv = process.env.AWS_EXECUTION_ENV; + if (!executionEnv?.startsWith('AWS_Lambda_')) { return Resource.empty(); } - const functionVersion = process.env.AWS_LAMBDA_FUNCTION_VERSION; - const region = process.env.AWS_REGION; + // These environment variables are guaranteed to be present in AWS Lambda + const region = process.env.AWS_REGION!; + const functionName = process.env.AWS_LAMBDA_FUNCTION_NAME!; + const functionVersion = process.env.AWS_LAMBDA_FUNCTION_VERSION!; + const memorySize = process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE!; const attributes: ResourceAttributes = { - [ATTR_CLOUD_PROVIDER]: String(CLOUD_PROVIDER_VALUE_AWS), - [ATTR_CLOUD_PLATFORM]: String(CLOUD_PLATFORM_VALUE_AWS_LAMBDA), + [ATTR_CLOUD_REGION]: region, + [ATTR_CLOUD_PROVIDER]: CLOUD_PROVIDER_VALUE_AWS, + [ATTR_CLOUD_PLATFORM]: CLOUD_PLATFORM_VALUE_AWS_LAMBDA, + [ATTR_FAAS_NAME]: functionName, + [ATTR_FAAS_VERSION]: functionVersion, + [ATTR_FAAS_MAX_MEMORY]: parseInt(memorySize) * 1024 * 1024, }; - if (region) { - attributes[ATTR_CLOUD_REGION] = region; - } - if (functionName) { - attributes[ATTR_FAAS_NAME] = functionName; + // These environment variables are not available in Lambda SnapStart + const logGroupName = process.env.AWS_LAMBDA_LOG_GROUP_NAME; + const logStreamName = process.env.AWS_LAMBDA_LOG_STREAM_NAME; + + if (logStreamName) { + attributes[ATTR_FAAS_INSTANCE] = logStreamName; } - if (functionVersion) { - attributes[ATTR_FAAS_VERSION] = functionVersion; + if (logGroupName) { + attributes[ATTR_AWS_LOG_GROUP_NAMES] = [logGroupName]; } return new Resource(attributes); diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetector.test.ts b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetector.test.ts index daaa34c61b..f4ba622fae 100644 --- a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetector.test.ts +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetector.test.ts @@ -35,6 +35,7 @@ describe('awsLambdaDetector', () => { describe('on lambda', () => { it('fills resource', async () => { + process.env.AWS_EXECUTION_ENV = 'AWS_Lambda_nodejs22.x'; process.env.AWS_LAMBDA_FUNCTION_NAME = 'name'; process.env.AWS_LAMBDA_FUNCTION_VERSION = 'v1'; process.env.AWS_REGION = 'us-east-1'; diff --git a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetectorSync.test.ts b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetectorSync.test.ts index e849dc436f..a3ab3ea959 100644 --- a/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetectorSync.test.ts +++ b/detectors/node/opentelemetry-resource-detector-aws/test/detectors/AwsLambdaDetectorSync.test.ts @@ -15,10 +15,19 @@ */ import * as assert from 'assert'; +import { assertEmptyResource } from '@opentelemetry/contrib-test-utils'; import { - assertCloudResource, - assertEmptyResource, -} from '@opentelemetry/contrib-test-utils'; + ATTR_CLOUD_PROVIDER, + ATTR_CLOUD_PLATFORM, + ATTR_CLOUD_REGION, + ATTR_FAAS_NAME, + ATTR_FAAS_VERSION, + ATTR_FAAS_INSTANCE, + ATTR_FAAS_MAX_MEMORY, + ATTR_AWS_LOG_GROUP_NAMES, + CLOUD_PROVIDER_VALUE_AWS, + CLOUD_PLATFORM_VALUE_AWS_LAMBDA, +} from '@opentelemetry/semantic-conventions/incubating'; import { awsLambdaDetectorSync } from '../../src'; @@ -35,25 +44,54 @@ describe('awsLambdaDetectorSync', () => { describe('on lambda', () => { it('fills resource', async () => { + process.env.AWS_EXECUTION_ENV = 'AWS_Lambda_nodejs22.x'; + process.env.AWS_REGION = 'us-east-1'; process.env.AWS_LAMBDA_FUNCTION_NAME = 'name'; process.env.AWS_LAMBDA_FUNCTION_VERSION = 'v1'; - process.env.AWS_REGION = 'us-east-1'; + process.env.AWS_LAMBDA_FUNCTION_MEMORY_SIZE = '128'; + process.env.AWS_LAMBDA_LOG_GROUP_NAME = '/aws/lambda/name'; + process.env.AWS_LAMBDA_LOG_STREAM_NAME = '2024/03/14/[$LATEST]123456'; const resource = awsLambdaDetectorSync.detect(); - assertCloudResource(resource, { - provider: 'aws', - region: 'us-east-1', - }); - - assert.strictEqual(resource.attributes['faas.name'], 'name'); - assert.strictEqual(resource.attributes['faas.version'], 'v1'); + assert.strictEqual(resource.attributes[ATTR_CLOUD_REGION], 'us-east-1'); + assert.strictEqual( + resource.attributes[ATTR_CLOUD_PROVIDER], + CLOUD_PROVIDER_VALUE_AWS + ); + assert.strictEqual( + resource.attributes[ATTR_CLOUD_PLATFORM], + CLOUD_PLATFORM_VALUE_AWS_LAMBDA + ); + assert.strictEqual(resource.attributes[ATTR_FAAS_NAME], 'name'); + assert.strictEqual(resource.attributes[ATTR_FAAS_VERSION], 'v1'); + assert.strictEqual( + resource.attributes[ATTR_FAAS_INSTANCE], + '2024/03/14/[$LATEST]123456' + ); + assert.strictEqual( + resource.attributes[ATTR_FAAS_MAX_MEMORY], + 128 * 1024 * 1024 + ); + assert.deepStrictEqual(resource.attributes[ATTR_AWS_LOG_GROUP_NAMES], [ + '/aws/lambda/name', + ]); }); }); describe('not on lambda', () => { - it('returns empty resource', async () => { - process.env.AWS_LAMBDA_FUNCTION_VERSION = 'v1'; + it('returns empty resource if AWS_EXECUTION_ENV is not set', async () => { + process.env.AWS_LAMBDA_FUNCTION_NAME = 'name'; + process.env.AWS_REGION = 'us-east-1'; + + const resource = awsLambdaDetectorSync.detect(); + + assertEmptyResource(resource); + }); + + it('returns empty resource if AWS_EXECUTION_ENV is not Lambda', async () => { + process.env.AWS_EXECUTION_ENV = 'AWS_ECS_EC2'; + process.env.AWS_LAMBDA_FUNCTION_NAME = 'name'; process.env.AWS_REGION = 'us-east-1'; const resource = awsLambdaDetectorSync.detect();