Skip to content

Commit

Permalink
Merge branch 'main' into contract_tests_feat_parity
Browse files Browse the repository at this point in the history
  • Loading branch information
mxiamxia authored Dec 16, 2024
2 parents 34b6ea5 + c58112e commit ff8476f
Show file tree
Hide file tree
Showing 24 changed files with 1,834 additions and 300 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/daily-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ jobs:
id: high_scan
uses: ./.github/actions/image_scan
with:
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.3.0"
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.4.0"
severity: 'CRITICAL,HIGH'

# TODO: Update image to public once available
Expand All @@ -82,7 +82,7 @@ jobs:
id: low_scan
uses: ./.github/actions/image_scan
with:
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.3.0"
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.4.0"
severity: 'MEDIUM,LOW,UNKNOWN'

- name: Configure AWS Credentials for emitting metrics
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
build
node_modules
.eslintrc.js
version.ts
version.ts
src/third-party
68 changes: 15 additions & 53 deletions aws-distro-opentelemetry-node-autoinstrumentation/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aws/aws-distro-opentelemetry-node-autoinstrumentation",
"version": "0.3.0-dev0",
"version": "0.4.0-dev0",
"description": "This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.",
"author": {
"name": "Amazon Web Services",
Expand Down Expand Up @@ -31,9 +31,18 @@
"prepublishOnly": "npm run compile",
"tdd": "yarn test -- --watch-extensions ts --watch",
"test": "nyc ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
"test:coverage": "nyc --all --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
"test:coverage": "nyc --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
"watch": "tsc -w"
},
"nyc": {
"all": true,
"include": [
"src/**/*.ts"
],
"exclude": [
"src/third-party/**/*.ts"
]
},
"bugs": {
"url": "https://github.com/aws-observability/aws-otel-js-instrumentation/issues"
},
Expand Down Expand Up @@ -64,7 +73,11 @@
"@aws-sdk/client-bedrock-agent-runtime": "3.632.0",
"@aws-sdk/client-bedrock-runtime": "3.632.0",
"@aws-sdk/client-kinesis": "3.632.0",
"@aws-sdk/client-lambda": "^3.632.0",
"@aws-sdk/client-s3": "3.632.0",
"@aws-sdk/client-secrets-manager": "^3.632.0",
"@aws-sdk/client-sfn": "^3.632.0",
"@aws-sdk/client-sns": "^3.632.0",
"@opentelemetry/contrib-test-utils": "0.41.0",
"@types/mocha": "7.0.2",
"@types/node": "18.6.5",
Expand Down Expand Up @@ -98,57 +111,6 @@
"@opentelemetry/sdk-trace-base": "1.26.0",
"@opentelemetry/semantic-conventions": "1.27.0"
},
"overrides": {
"@opentelemetry/auto-instrumentations-node": {
"@opentelemetry/instrumentation": "0.53.0",
"@opentelemetry/instrumentation-amqplib": "0.42.0",
"@opentelemetry/instrumentation-aws-lambda": "0.44.0",
"@opentelemetry/instrumentation-aws-sdk": "0.44.0",
"@opentelemetry/instrumentation-bunyan": "0.41.0",
"@opentelemetry/instrumentation-cassandra-driver": "0.41.0",
"@opentelemetry/instrumentation-connect": "0.39.0",
"@opentelemetry/instrumentation-cucumber": "0.9.0",
"@opentelemetry/instrumentation-dataloader": "0.12.0",
"@opentelemetry/instrumentation-dns": "0.39.0",
"@opentelemetry/instrumentation-express": "0.42.0",
"@opentelemetry/instrumentation-fastify": "0.39.0",
"@opentelemetry/instrumentation-fs": "0.15.0",
"@opentelemetry/instrumentation-generic-pool": "0.39.0",
"@opentelemetry/instrumentation-graphql": "0.43.0",
"@opentelemetry/instrumentation-grpc": "0.53.0",
"@opentelemetry/instrumentation-hapi": "0.41.0",
"@opentelemetry/instrumentation-http": "0.53.0",
"@opentelemetry/instrumentation-ioredis": "0.43.0",
"@opentelemetry/instrumentation-kafkajs": "0.3.0",
"@opentelemetry/instrumentation-knex": "0.40.0",
"@opentelemetry/instrumentation-koa": "0.43.0",
"@opentelemetry/instrumentation-lru-memoizer": "0.40.0",
"@opentelemetry/instrumentation-memcached": "0.39.0",
"@opentelemetry/instrumentation-mongodb": "0.47.0",
"@opentelemetry/instrumentation-mongoose": "0.42.0",
"@opentelemetry/instrumentation-mysql": "0.41.0",
"@opentelemetry/instrumentation-mysql2": "0.41.0",
"@opentelemetry/instrumentation-nestjs-core": "0.40.0",
"@opentelemetry/instrumentation-net": "0.39.0",
"@opentelemetry/instrumentation-pg": "0.44.0",
"@opentelemetry/instrumentation-pino": "0.42.0",
"@opentelemetry/instrumentation-redis": "0.42.0",
"@opentelemetry/instrumentation-redis-4": "0.42.0",
"@opentelemetry/instrumentation-restify": "0.41.0",
"@opentelemetry/instrumentation-router": "0.40.0",
"@opentelemetry/instrumentation-socket.io": "0.42.0",
"@opentelemetry/instrumentation-tedious": "0.14.0",
"@opentelemetry/instrumentation-undici": "0.6.0",
"@opentelemetry/instrumentation-winston": "0.40.0",
"@opentelemetry/resource-detector-alibaba-cloud": "0.29.1",
"@opentelemetry/resource-detector-aws": "1.6.1",
"@opentelemetry/resource-detector-azure": "0.2.11",
"@opentelemetry/resource-detector-container": "0.4.1",
"@opentelemetry/resource-detector-gcp": "0.29.11",
"@opentelemetry/resources": "1.26.0",
"@opentelemetry/sdk-node": "0.53.0"
}
},
"files": [
"build/src/**/*.js",
"build/src/**/*.js.map",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = {
AWS_REMOTE_RESOURCE_IDENTIFIER: 'aws.remote.resource.identifier',
AWS_SDK_DESCENDANT: 'aws.sdk.descendant',
AWS_CONSUMER_PARENT_SPAN_KIND: 'aws.consumer.parent.span.kind',
AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER: 'aws.remote.resource.cfn.primary.identifier',

AWS_REMOTE_TARGET: 'aws.remote.target',
AWS_REMOTE_DB_USER: 'aws.remote.db.user',
Expand All @@ -35,4 +36,12 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = {
AWS_BEDROCK_KNOWLEDGE_BASE_ID: 'aws.bedrock.knowledge_base.id',
AWS_BEDROCK_AGENT_ID: 'aws.bedrock.agent.id',
AWS_BEDROCK_GUARDRAIL_ID: 'aws.bedrock.guardrail.id',
AWS_BEDROCK_GUARDRAIL_ARN: 'aws.bedrock.guardrail.arn',
AWS_SNS_TOPIC_ARN: 'aws.sns.topic.arn',
AWS_SECRETSMANAGER_SECRET_ARN: 'aws.secretsmanager.secret.arn',
AWS_STEPFUNCTIONS_STATEMACHINE_ARN: 'aws.stepfunctions.state_machine.arn',
AWS_STEPFUNCTIONS_ACTIVITY_ARN: 'aws.stepfunctions.activity.arn',
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',
};
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ const NORMALIZED_DYNAMO_DB_SERVICE_NAME: string = 'AWS::DynamoDB';
const NORMALIZED_KINESIS_SERVICE_NAME: string = 'AWS::Kinesis';
const NORMALIZED_S3_SERVICE_NAME: string = 'AWS::S3';
const NORMALIZED_SQS_SERVICE_NAME: string = 'AWS::SQS';
const NORMALIZED_SNS_SERVICE_NAME: string = 'AWS::SNS';
const NORMALIZED_SECRETSMANAGER_SERVICE_NAME = 'AWS::SecretsManager';
const NORMALIZED_STEPFUNCTIONS_SERVICE_NAME = 'AWS::StepFunctions';
const NORMALIZED_LAMBDA_SERVICE_NAME = 'AWS::Lambda';
const NORMALIZED_BEDROCK_SERVICE_NAME: string = 'AWS::Bedrock';
const NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME: string = 'AWS::BedrockRuntime';

Expand Down Expand Up @@ -330,6 +334,8 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
BedrockAgent: NORMALIZED_BEDROCK_SERVICE_NAME,
BedrockAgentRuntime: NORMALIZED_BEDROCK_SERVICE_NAME,
BedrockRuntime: NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME,
SecretsManager: NORMALIZED_SECRETSMANAGER_SERVICE_NAME,
SFN: NORMALIZED_STEPFUNCTIONS_SERVICE_NAME,
};
return awsSdkServiceMapping[serviceName] || 'AWS::' + serviceName;
}
Expand All @@ -350,6 +356,7 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
private static setRemoteResourceTypeAndIdentifier(span: ReadableSpan, attributes: Attributes): void {
let remoteResourceType: AttributeValue | undefined;
let remoteResourceIdentifier: AttributeValue | undefined;
let cloudFormationIdentifier: AttributeValue | undefined;

if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) {
const awsTableNames: AttributeValue | undefined = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_DYNAMODB_TABLE_NAMES];
Expand All @@ -370,16 +377,56 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_S3_BUCKET]
);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN)) {
const snsArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SNS_TOPIC_ARN];

remoteResourceType = NORMALIZED_SNS_SERVICE_NAME + '::Topic';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
this.extractResourceNameFromArn(snsArn)
);
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(snsArn);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN)) {
const secretsArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN];

remoteResourceType = NORMALIZED_SECRETSMANAGER_SERVICE_NAME + '::Secret';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
this.extractResourceNameFromArn(secretsArn)
);
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(secretsArn);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN)) {
const stateMachineArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_STATEMACHINE_ARN];

remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::StateMachine';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
this.extractResourceNameFromArn(stateMachineArn)
);
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(stateMachineArn);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN)) {
const activityArn = span.attributes[AWS_ATTRIBUTE_KEYS.AWS_STEPFUNCTIONS_ACTIVITY_ARN];

remoteResourceType = NORMALIZED_STEPFUNCTIONS_SERVICE_NAME + '::Activity';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
this.extractResourceNameFromArn(activityArn)
);
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(activityArn);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID)) {
remoteResourceType = NORMALIZED_LAMBDA_SERVICE_NAME + '::EventSourceMapping';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_LAMBDA_RESOURCE_MAPPING_ID]
);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME)) {
remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_NAME]
);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL)) {
remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue';
remoteResourceIdentifier = SqsUrlParser.getQueueName(
AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL])
const sqsQueueUrl = AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL]
);

remoteResourceType = NORMALIZED_SQS_SERVICE_NAME + '::Queue';
remoteResourceIdentifier = SqsUrlParser.getQueueName(sqsQueueUrl);
cloudFormationIdentifier = sqsQueueUrl;
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Agent';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
Expand All @@ -390,11 +437,17 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]
);
cloudFormationIdentifier = `${AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]
)}|${remoteResourceIdentifier}`;
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Guardrail';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID]
);
cloudFormationIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN]
);
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID)) {
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::KnowledgeBase';
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
Expand All @@ -414,6 +467,14 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
if (remoteResourceType !== undefined && remoteResourceIdentifier !== undefined) {
attributes[AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_TYPE] = remoteResourceType;
attributes[AWS_ATTRIBUTE_KEYS.AWS_REMOTE_RESOURCE_IDENTIFIER] = remoteResourceIdentifier;

if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) {
if (cloudFormationIdentifier === undefined) {
cloudFormationIdentifier = remoteResourceIdentifier;
}

attributes[AWS_ATTRIBUTE_KEYS.AWS_CLOUDFORMATION_PRIMARY_IDENTIFIER] = cloudFormationIdentifier;
}
}
}

Expand Down Expand Up @@ -532,6 +593,16 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
return input.split('^').join('^^').split('|').join('^|');
}

// Extracts the name of the resource from an arn
private static extractResourceNameFromArn(attribute: AttributeValue | undefined): string | undefined {
if (typeof attribute === 'string' && attribute.startsWith('arn:aws:')) {
const split = attribute.split(':');
return split[split.length - 1];
}

return undefined;
}

/** Span kind is needed for differentiating metrics in the EMF exporter */
private static setSpanKindForService(span: ReadableSpan, attributes: Attributes): void {
let spanKind: string = SpanKind[span.kind];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ const AGENT_ID: string = 'agentId';
const KNOWLEDGE_BASE_ID: string = 'knowledgeBaseId';
const DATA_SOURCE_ID: string = 'dataSourceId';
const GUARDRAIL_ID: string = 'guardrailId';
const GUARDRAIL_ARN: string = 'guardrailArn';
const MODEL_ID: string = 'modelId';
const AWS_BEDROCK_SYSTEM: string = 'aws_bedrock';
const AWS_BEDROCK_SYSTEM: string = 'aws.bedrock';

const AGENT_OPERATIONS = [
'CreateAgentActionGroup',
Expand Down Expand Up @@ -60,6 +61,7 @@ const knowledgeBaseOperationAttributeKeyMapping = {
};
const dataSourceOperationAttributeKeyMapping = {
[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]: DATA_SOURCE_ID,
[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]: KNOWLEDGE_BASE_ID,
};

// This map allows us to get all relevant attribute key mappings for a given operation
Expand Down Expand Up @@ -182,10 +184,15 @@ export class BedrockServiceExtension implements ServiceExtension {
};
}
responseHook(response: NormalizedResponse, span: Span, tracer: Tracer, config: AwsSdkInstrumentationConfig): void {
const guardrail_id = response.data[GUARDRAIL_ID];
const guardrailId = response.data[GUARDRAIL_ID];
const guardrailArn = response.data[GUARDRAIL_ARN];

if (guardrailId) {
span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID, guardrailId);
}

if (guardrail_id) {
span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID, guardrail_id);
if (guardrailArn) {
span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ARN, guardrailArn);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { Attributes, Span, SpanKind, Tracer } from '@opentelemetry/api';
import {
AwsSdkInstrumentationConfig,
NormalizedRequest,
NormalizedResponse,
} from '@opentelemetry/instrumentation-aws-sdk';
import { AWS_ATTRIBUTE_KEYS } from '../../../aws-attribute-keys';
import { RequestMetadata, ServiceExtension } from '../../../third-party/otel/aws/services/ServiceExtension';

export class SecretsManagerServiceExtension implements ServiceExtension {
requestPreSpanHook(request: NormalizedRequest, _config: AwsSdkInstrumentationConfig): RequestMetadata {
const secretId = request.commandInput?.SecretId;

const spanKind: SpanKind = SpanKind.CLIENT;
let spanName: string | undefined;

const spanAttributes: Attributes = {};

if (typeof secretId === 'string' && secretId.startsWith('arn:aws:secretsmanager:')) {
spanAttributes[AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN] = secretId;
}

const isIncoming = false;

return {
isIncoming,
spanAttributes,
spanKind,
spanName,
};
}

responseHook(response: NormalizedResponse, span: Span, tracer: Tracer, config: AwsSdkInstrumentationConfig): void {
const secretArn = response.data.ARN;

if (secretArn) {
span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_SECRETSMANAGER_SECRET_ARN, secretArn);
}
}
}
Loading

0 comments on commit ff8476f

Please sign in to comment.