diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts index 54bd517a..fde4ae2a 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts @@ -4,7 +4,7 @@ import { SEMATTRS_AWS_DYNAMODB_TABLE_NAMES } from '@opentelemetry/semantic-conventions'; // Utility class holding attribute keys with special meaning to AWS components -export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = { +export const AWS_ATTRIBUTE_KEYS = { AWS_SPAN_KIND: 'aws.span.kind', AWS_LOCAL_SERVICE: 'aws.local.service', AWS_LOCAL_OPERATION: 'aws.local.operation', @@ -45,4 +45,5 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = { AWS_LAMBDA_FUNCTION_NAME: 'aws.lambda.function.name', AWS_LAMBDA_RESOURCE_MAPPING_ID: 'aws.lambda.resource_mapping.id', AWS_LAMBDA_FUNCTION_ARN: 'aws.lambda.function.arn', + AWS_SERVICE_TYPE: 'aws.service.type', }; diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-opentelemetry-configurator.ts b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-opentelemetry-configurator.ts index f453e325..7860f29d 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-opentelemetry-configurator.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/src/aws-opentelemetry-configurator.ts @@ -78,6 +78,7 @@ import { OTLPAwsLogExporter } from './exporter/otlp/aws/logs/otlp-aws-log-export import { isAgentObservabilityEnabled } from './utils'; import { BaggageSpanProcessor } from '@opentelemetry/baggage-span-processor'; import { logs } from '@opentelemetry/api-logs'; +import { AWS_ATTRIBUTE_KEYS } from './aws-attribute-keys'; const AWS_TRACES_OTLP_ENDPOINT_PATTERN = '^https://xray\\.([a-z0-9-]+)\\.amazonaws\\.com/v1/traces$'; const AWS_LOGS_OTLP_ENDPOINT_PATTERN = '^https://logs\\.([a-z0-9-]+)\\.amazonaws\\.com/v1/logs$'; @@ -168,8 +169,8 @@ export class AwsOpentelemetryConfigurator { if (!resourceDetectorsFromEnv.includes('env')) { defaultDetectors.push(envDetectorSync); } - } else if (isLambdaEnvironment()) { - // If in Lambda environment, only keep env detector as default + } else if (isLambdaEnvironment() || isAgentObservabilityEnabled()) { + // Only keep env detector here defaultDetectors.push(envDetectorSync); } else { /* @@ -193,7 +194,7 @@ export class AwsOpentelemetryConfigurator { detectors: defaultDetectors, }; - autoResource = autoResource.merge(detectResourcesSync(internalConfig)); + autoResource = this.customizeResource(autoResource.merge(detectResourcesSync(internalConfig))); this.resource = autoResource; this.instrumentations = instrumentations; @@ -228,6 +229,17 @@ export class AwsOpentelemetryConfigurator { return autoResource; } + private customizeResource(resource: Resource) { + if (isAgentObservabilityEnabled()) { + // Add aws.service.type if it doesn't exist in the resource + if (!resource.attributes[AWS_ATTRIBUTE_KEYS.AWS_SERVICE_TYPE]) { + // Set a default agent type for AI agent observability + resource.attributes[AWS_ATTRIBUTE_KEYS.AWS_SERVICE_TYPE] = 'gen_ai_agent'; + } + } + return resource; + } + public configure(): Partial { // config.autoDetectResources is set to False, as the resources are detected and added to the // resource ahead of time. The resource is needed to be populated ahead of time instead of letting diff --git a/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-opentelemetry-configurator.test.ts b/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-opentelemetry-configurator.test.ts index 5d422c81..c1ebe882 100644 --- a/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-opentelemetry-configurator.test.ts +++ b/aws-distro-opentelemetry-node-autoinstrumentation/test/aws-opentelemetry-configurator.test.ts @@ -52,6 +52,8 @@ import { AwsXRayRemoteSampler } from '../src/sampler/aws-xray-remote-sampler'; import { AwsXraySamplingClient } from '../src/sampler/aws-xray-sampling-client'; import { GetSamplingRulesResponse } from '../src/sampler/remote-sampler.types'; import { BaggageSpanProcessor } from '@opentelemetry/baggage-span-processor'; +import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'; +import { AWS_ATTRIBUTE_KEYS } from '../src/aws-attribute-keys'; import { BatchLogRecordProcessor, ConsoleLogRecordExporter, @@ -1183,4 +1185,38 @@ describe('AwsOpenTelemetryConfiguratorTest', () => { delete process.env[key]; } } + + it('CustomizeResourceWithoutAgentObservability', () => { + delete process.env.AGENT_OBSERVABILITY_ENABLED; + + let resource = new Resource({ [ATTR_SERVICE_NAME]: 'test-service' }); + resource = awsOtelConfigurator['customizeResource'](resource); + expect(resource.attributes[ATTR_SERVICE_NAME]).toEqual('test-service'); + expect(resource.attributes).not.toHaveProperty(AWS_ATTRIBUTE_KEYS.AWS_SERVICE_TYPE); + }); + + it('CustomizeResourceWithAgentObservabilityDefault', () => { + process.env.AGENT_OBSERVABILITY_ENABLED = 'true'; + + let resource = new Resource({ [ATTR_SERVICE_NAME]: 'test-service' }); + resource = awsOtelConfigurator['customizeResource'](resource); + expect(resource.attributes[ATTR_SERVICE_NAME]).toEqual('test-service'); + expect(resource.attributes[AWS_ATTRIBUTE_KEYS.AWS_SERVICE_TYPE]).toEqual('gen_ai_agent'); + + delete process.env.AGENT_OBSERVABILITY_ENABLED; + }); + + it('CustomizeResourceWithoutAgentObservability', () => { + process.env.AGENT_OBSERVABILITY_ENABLED = 'true'; + + let resource = new Resource({ + [ATTR_SERVICE_NAME]: 'test-service', + [AWS_ATTRIBUTE_KEYS.AWS_SERVICE_TYPE]: 'existing-agent', + }); + resource = awsOtelConfigurator['customizeResource'](resource); + expect(resource.attributes[ATTR_SERVICE_NAME]).toEqual('test-service'); + expect(resource.attributes[AWS_ATTRIBUTE_KEYS.AWS_SERVICE_TYPE]).toEqual('existing-agent'); + + delete process.env.AGENT_OBSERVABILITY_ENABLED; + }); });