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 66fe94ee2a..1f7176ce8c 100644 --- a/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsLambdaDetectorSync.ts +++ b/detectors/node/opentelemetry-resource-detector-aws/src/detectors/AwsLambdaDetectorSync.ts @@ -22,11 +22,14 @@ import { ResourceDetectionConfig, } from '@opentelemetry/resources'; import { + ATTR_AWS_LOG_GROUP_NAMES, ATTR_CLOUD_PROVIDER, ATTR_CLOUD_PLATFORM, ATTR_CLOUD_REGION, - ATTR_FAAS_VERSION, ATTR_FAAS_NAME, + ATTR_FAAS_VERSION, + ATTR_FAAS_INSTANCE, + ATTR_FAAS_MAX_MEMORY, CLOUD_PROVIDER_VALUE_AWS, CLOUD_PLATFORM_VALUE_AWS_LAMBDA, } from '../semconv'; @@ -38,27 +41,37 @@ import { */ 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; + // These environment variables are guaranteed to be present in Lambda environment + // https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime 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; + + // These environment variables are not available in Lambda SnapStart functions + const logGroupName = process.env.AWS_LAMBDA_LOG_GROUP_NAME; + const logStreamName = process.env.AWS_LAMBDA_LOG_STREAM_NAME; const attributes: ResourceAttributes = { - [ATTR_CLOUD_PROVIDER]: String(CLOUD_PROVIDER_VALUE_AWS), - [ATTR_CLOUD_PLATFORM]: String(CLOUD_PLATFORM_VALUE_AWS_LAMBDA), + [ATTR_CLOUD_PROVIDER]: CLOUD_PROVIDER_VALUE_AWS, + [ATTR_CLOUD_PLATFORM]: CLOUD_PLATFORM_VALUE_AWS_LAMBDA, + [ATTR_CLOUD_REGION]: region, + [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; + if (logGroupName) { + attributes[ATTR_AWS_LOG_GROUP_NAMES] = [logGroupName]; } - if (functionVersion) { - attributes[ATTR_FAAS_VERSION] = functionVersion; + if (logStreamName) { + attributes[ATTR_FAAS_INSTANCE] = logStreamName; } return new Resource(attributes); diff --git a/detectors/node/opentelemetry-resource-detector-aws/src/semconv.ts b/detectors/node/opentelemetry-resource-detector-aws/src/semconv.ts index 3d34526643..d12c459a10 100644 --- a/detectors/node/opentelemetry-resource-detector-aws/src/semconv.ts +++ b/detectors/node/opentelemetry-resource-detector-aws/src/semconv.ts @@ -210,6 +210,28 @@ export const ATTR_CONTAINER_NAME = 'container.name'; */ export const ATTR_FAAS_NAME = 'faas.name'; +/** + * The execution environment ID as a string, that will be potentially reused for other invocations to the same function/function version. + * + * @example 2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de + * + * @note * **AWS Lambda:** Use the (full) log stream name. + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + */ +export const ATTR_FAAS_INSTANCE = 'faas.instance'; + +/** + * The amount of memory available to the serverless function converted to Bytes. + * + * @example 134217728 + * + * @note It's recommended to set this attribute since e.g. too little memory can easily stop a Java AWS Lambda function from working correctly. On AWS Lambda, the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this information (which must be multiplied by 1,048,576). + * + * @experimental This attribute is experimental and is subject to breaking changes in minor releases of `@opentelemetry/semantic-conventions`. + */ +export const ATTR_FAAS_MAX_MEMORY = 'faas.max_memory'; + /** * The immutable version of the function being executed. * 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..db468cb707 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,12 +15,20 @@ */ import * as assert from 'assert'; -import { - assertCloudResource, - assertEmptyResource, -} from '@opentelemetry/contrib-test-utils'; - +import { assertEmptyResource } from '@opentelemetry/contrib-test-utils'; import { awsLambdaDetectorSync } from '../../src'; +import { + 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 '../../src/semconv'; describe('awsLambdaDetectorSync', () => { let oldEnv: NodeJS.ProcessEnv; @@ -35,25 +43,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_PROVIDER], + CLOUD_PROVIDER_VALUE_AWS + ); + assert.strictEqual( + resource.attributes[ATTR_CLOUD_PLATFORM], + CLOUD_PLATFORM_VALUE_AWS_LAMBDA + ); + assert.strictEqual(resource.attributes[ATTR_CLOUD_REGION], 'us-east-1'); + 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();