Skip to content

Commit f9d5a7c

Browse files
authored
Merge branch 'aws-observability:main' into main
2 parents ae738b7 + d3b5825 commit f9d5a7c

File tree

73 files changed

+5128
-194
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+5128
-194
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
## Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
## SPDX-License-Identifier: Apache-2.0
3+
4+
# This is a reusable workflow for running the E2E test for Application Signals.
5+
# It is meant to be called from another workflow.
6+
# Read more about reusable workflows: https://docs.github.com/en/actions/using-workflows/reusing-workflows#overview
7+
name: E2E Testing
8+
on:
9+
workflow_call:
10+
inputs:
11+
staging-instrumentation-name:
12+
required: true
13+
type: string
14+
adot-image-name:
15+
required: true
16+
type: string
17+
18+
permissions:
19+
id-token: write
20+
contents: read
21+
22+
concurrency:
23+
group: '${{ github.workflow }} @ ${{ inputs.aws-region }}'
24+
cancel-in-progress: false
25+
26+
27+
jobs:
28+
upload-main-build:
29+
runs-on: ubuntu-latest
30+
steps:
31+
- name: Configure AWS Credentials
32+
uses: aws-actions/configure-aws-credentials@v4
33+
with:
34+
role-to-assume: arn:aws:iam::637423224110:role/${{ secrets.STAGING_ARTIFACTS_ACCESS_ROLE_NAME }}
35+
aws-region: us-east-1
36+
37+
- uses: actions/download-artifact@v3
38+
with:
39+
name: ${{ inputs.staging-instrumentation-name }}
40+
41+
- name: Upload main-build instrumentation to S3
42+
run: aws s3 cp ${{ inputs.staging-instrumentation-name }} s3://adot-autoinstrumentation-node-staging/${{ inputs.staging-instrumentation-name }}
43+
44+
ec2-default:
45+
needs: [ upload-main-build ]
46+
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/node-ec2-default-test.yml@main
47+
secrets: inherit
48+
with:
49+
aws-region: us-east-1
50+
staging-instrumentation-name: ${{ inputs.staging-instrumentation-name }}
51+
caller-workflow-name: 'main-build'
52+
53+
ec2-asg:
54+
needs: [ upload-main-build ]
55+
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/node-ec2-asg-test.yml@main
56+
secrets: inherit
57+
with:
58+
aws-region: us-east-1
59+
staging-instrumentation-name: ${{ inputs.staging-instrumentation-name }}
60+
caller-workflow-name: 'main-build'
61+
62+
eks:
63+
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/node-eks-test.yml@main
64+
secrets: inherit
65+
with:
66+
aws-region: us-east-1
67+
test-cluster-name: 'e2e-node-adot-test'
68+
adot-image-name: ${{ inputs.adot-image-name }}
69+
caller-workflow-name: 'main-build'
70+
71+
k8s:
72+
uses: aws-observability/aws-application-signals-test-framework/.github/workflows/node-k8s-test.yml@main
73+
secrets: inherit
74+
with:
75+
aws-region: us-east-1
76+
adot-image-name: ${{ inputs.adot-image-name }}
77+
caller-workflow-name: 'main-build'

.github/workflows/daily-scan.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ jobs:
7373
id: high_scan
7474
uses: ./.github/actions/image_scan
7575
with:
76-
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.1.0"
76+
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.2.0"
7777
severity: 'CRITICAL,HIGH'
7878

7979
# TODO: Update image to public once available
@@ -82,7 +82,7 @@ jobs:
8282
id: low_scan
8383
uses: ./.github/actions/image_scan
8484
with:
85-
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.1.0"
85+
image-ref: "public.ecr.aws/aws-observability/adot-autoinstrumentation-node:v0.2.0"
8686
severity: 'MEDIUM,LOW,UNKNOWN'
8787

8888
- name: Configure AWS Credentials for emitting metrics

.github/workflows/main-build.yml

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ on:
55
branches:
66
- main
77
- "release/v*"
8-
- e2e-parallel
98

109
env:
1110
AWS_DEFAULT_REGION: us-east-1
@@ -84,15 +83,14 @@ jobs:
8483
# pip install pytest
8584
# pytest contract-tests/tests
8685

87-
# TODO - implement E2E tests in NodeJS
88-
# application-signals-e2e-test:
89-
# name: "Application Signals E2E Test"
90-
# needs: [ build ]
91-
# uses: ./.github/workflows/application-signals-e2e-test.yml
92-
# secrets: inherit
93-
# permissions:
94-
# id-token: write
95-
# contents: read
96-
# with:
97-
# staging-wheel-name: ${{ needs.build.outputs.staging_wheel_file }}
98-
# adot-image-name: ${{ needs.build.outputs.staging_registry }}/aws-observability/adot-autoinstrumentation-python-staging:${{ needs.build.outputs.python_image_tag }}
86+
application-signals-e2e-test:
87+
name: "Application Signals E2E Test"
88+
needs: [ build ]
89+
uses: ./.github/workflows/application-signals-e2e-test.yml
90+
secrets: inherit
91+
permissions:
92+
id-token: write
93+
contents: read
94+
with:
95+
staging-instrumentation-name: ${{ needs.build.outputs.staging_tarball_file }}
96+
adot-image-name: ${{ needs.build.outputs.staging_registry }}/aws-observability/adot-autoinstrumentation-node-staging:${{ needs.build.outputs.node_image_tag }}

aws-distro-opentelemetry-node-autoinstrumentation/package.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@aws/aws-distro-opentelemetry-node-autoinstrumentation",
3-
"version": "0.1.0-dev0",
3+
"version": "0.2.0-dev0",
44
"description": "This package provides Amazon Web Services distribution of the OpenTelemetry Node Instrumentation, which allows for auto-instrumentation of NodeJS applications.",
55
"author": {
66
"name": "Amazon Web Services",
@@ -37,7 +37,32 @@
3737
"bugs": {
3838
"url": "https://github.com/aws-observability/aws-otel-js-instrumentation/issues"
3939
},
40+
"keywords": [
41+
"aws",
42+
"amazon",
43+
"adot",
44+
"adotjs",
45+
"adot-js",
46+
"adot js",
47+
"xray",
48+
"x-ray",
49+
"x ray",
50+
"awsxray",
51+
"awsdistroopentelemetry",
52+
"opentelemetry",
53+
"otel",
54+
"awslambda",
55+
"nodejs",
56+
"trace",
57+
"tracing",
58+
"profiling",
59+
"instrumentation"
60+
],
4061
"devDependencies": {
62+
"@aws-sdk/client-bedrock": "3.632.0",
63+
"@aws-sdk/client-bedrock-agent": "3.632.0",
64+
"@aws-sdk/client-bedrock-agent-runtime": "3.632.0",
65+
"@aws-sdk/client-bedrock-runtime": "3.632.0",
4166
"@aws-sdk/client-kinesis": "3.632.0",
4267
"@aws-sdk/client-s3": "3.632.0",
4368
"@aws-sdk/client-sqs": "3.632.0",

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,8 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = {
3131
AWS_SQS_QUEUE_NAME: 'aws.sqs.queue.name',
3232
AWS_KINESIS_STREAM_NAME: 'aws.kinesis.stream.name',
3333
AWS_DYNAMODB_TABLE_NAMES: SEMATTRS_AWS_DYNAMODB_TABLE_NAMES,
34+
AWS_BEDROCK_DATA_SOURCE_ID: 'aws.bedrock.data_source.id',
35+
AWS_BEDROCK_KNOWLEDGE_BASE_ID: 'aws.bedrock.knowledge_base.id',
36+
AWS_BEDROCK_AGENT_ID: 'aws.bedrock.agent.id',
37+
AWS_BEDROCK_GUARDRAIL_ID: 'aws.bedrock.guardrail.id',
3438
};

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-metric-attribute-generator.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ const NORMALIZED_DYNAMO_DB_SERVICE_NAME: string = 'AWS::DynamoDB';
5555
const NORMALIZED_KINESIS_SERVICE_NAME: string = 'AWS::Kinesis';
5656
const NORMALIZED_S3_SERVICE_NAME: string = 'AWS::S3';
5757
const NORMALIZED_SQS_SERVICE_NAME: string = 'AWS::SQS';
58+
const NORMALIZED_BEDROCK_SERVICE_NAME: string = 'AWS::Bedrock';
59+
const NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME: string = 'AWS::BedrockRuntime';
5860

5961
const DB_CONNECTION_RESOURCE_TYPE: string = 'DB::Connection';
6062
// As per https://opentelemetry.io/docs/specs/semconv/resource/#service, if service name is not specified, SDK defaults
@@ -317,10 +319,19 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
317319
* Cloud Control resource format</a> as much as possible, with special attention to services we
318320
* can detect remote resource information for. Long term, we would like to normalize service name
319321
* in the upstream.
322+
*
323+
* For Bedrock, Bedrock Agent, and Bedrock Agent Runtime, we can align with AWS Cloud Control and use
324+
* AWS::Bedrock for RemoteService. For BedrockRuntime, we are using AWS::BedrockRuntime
325+
* as the associated remote resource (Model) is not listed in Cloud Control.
320326
*/
321327
private static normalizeRemoteServiceName(span: ReadableSpan, serviceName: string): string {
322328
if (AwsSpanProcessingUtil.isAwsSDKSpan(span)) {
323-
return 'AWS::' + serviceName;
329+
const awsSdkServiceMapping: { [key: string]: string } = {
330+
BedrockAgent: NORMALIZED_BEDROCK_SERVICE_NAME,
331+
BedrockAgentRuntime: NORMALIZED_BEDROCK_SERVICE_NAME,
332+
BedrockRuntime: NORMALIZED_BEDROCK_RUNTIME_SERVICE_NAME,
333+
};
334+
return awsSdkServiceMapping[serviceName] || 'AWS::' + serviceName;
324335
}
325336
return serviceName;
326337
}
@@ -369,6 +380,31 @@ export class AwsMetricAttributeGenerator implements MetricAttributeGenerator {
369380
remoteResourceIdentifier = SqsUrlParser.getQueueName(
370381
AwsMetricAttributeGenerator.escapeDelimiters(span.attributes[AWS_ATTRIBUTE_KEYS.AWS_SQS_QUEUE_URL])
371382
);
383+
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID)) {
384+
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Agent';
385+
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
386+
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_AGENT_ID]
387+
);
388+
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID)) {
389+
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::DataSource';
390+
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
391+
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_DATA_SOURCE_ID]
392+
);
393+
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID)) {
394+
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Guardrail';
395+
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
396+
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_GUARDRAIL_ID]
397+
);
398+
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID)) {
399+
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::KnowledgeBase';
400+
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
401+
span.attributes[AWS_ATTRIBUTE_KEYS.AWS_BEDROCK_KNOWLEDGE_BASE_ID]
402+
);
403+
} else if (AwsSpanProcessingUtil.isKeyPresent(span, AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL)) {
404+
remoteResourceType = NORMALIZED_BEDROCK_SERVICE_NAME + '::Model';
405+
remoteResourceIdentifier = AwsMetricAttributeGenerator.escapeDelimiters(
406+
span.attributes[AwsSpanProcessingUtil.GEN_AI_REQUEST_MODEL]
407+
);
372408
}
373409
} else if (AwsSpanProcessingUtil.isDBSpan(span)) {
374410
remoteResourceType = DB_CONNECTION_RESOURCE_TYPE;

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-opentelemetry-configurator.ts

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export class AwsOpentelemetryConfigurator {
104104
* @constructor
105105
* @param {Instrumentation[]} instrumentations - Auto-Instrumentations to be added to the ADOT Config
106106
*/
107-
public constructor(instrumentations: Instrumentation[]) {
107+
public constructor(instrumentations: Instrumentation[], useXraySampler: boolean = false) {
108108
/*
109109
* Set and Detect Resources via Resource Detectors
110110
*
@@ -167,7 +167,9 @@ export class AwsOpentelemetryConfigurator {
167167
// https://github.com/aws-observability/aws-otel-java-instrumentation/blob/a011b8cc29ee32b7f668c04ccfdf64cd30de467c/awsagentprovider/src/main/java/software/amazon/opentelemetry/javaagent/providers/AwsTracerCustomizerProvider.java#L36
168168
this.idGenerator = new AWSXRayIdGenerator();
169169

170-
this.sampler = AwsOpentelemetryConfigurator.customizeSampler(customBuildSamplerFromEnv(this.resource));
170+
this.sampler = AwsOpentelemetryConfigurator.customizeSampler(
171+
customBuildSamplerFromEnv(this.resource, useXraySampler)
172+
);
171173

172174
// default SpanProcessors with Span Exporters wrapped inside AwsMetricAttributesSpanExporter
173175
const awsSpanProcessorProvider: AwsSpanProcessorProvider = new AwsSpanProcessorProvider(this.resource);
@@ -273,7 +275,13 @@ export class AwsOpentelemetryConfigurator {
273275
resource: resource,
274276
readers: [periodicExportingMetricReader],
275277
});
276-
spanProcessors.push(AwsSpanMetricsProcessorBuilder.create(meterProvider, resource).build());
278+
spanProcessors.push(
279+
AwsSpanMetricsProcessorBuilder.create(
280+
meterProvider,
281+
resource,
282+
meterProvider.forceFlush.bind(meterProvider)
283+
).build()
284+
);
277285
}
278286
}
279287

@@ -285,37 +293,36 @@ export class AwsOpentelemetryConfigurator {
285293
}
286294
}
287295

288-
export function customBuildSamplerFromEnv(resource: Resource): Sampler {
289-
switch (process.env.OTEL_TRACES_SAMPLER) {
290-
case 'xray': {
291-
const samplerArgumentEnv: string | undefined = process.env.OTEL_TRACES_SAMPLER_ARG;
292-
let endpoint: string | undefined = undefined;
293-
let pollingInterval: number | undefined = undefined;
294-
295-
if (samplerArgumentEnv !== undefined) {
296-
const args: string[] = samplerArgumentEnv.split(',');
297-
for (const arg of args) {
298-
const equalIndex: number = arg.indexOf('=');
299-
if (equalIndex === -1) {
300-
continue;
301-
}
302-
const keyValue: string[] = [arg.substring(0, equalIndex), arg.substring(equalIndex + 1)];
303-
if (keyValue[0] === 'endpoint') {
304-
endpoint = keyValue[1];
305-
} else if (keyValue[0] === 'polling_interval') {
306-
pollingInterval = Number(keyValue[1]);
307-
if (isNaN(pollingInterval)) {
308-
pollingInterval = undefined;
309-
diag.error('polling_interval in OTEL_TRACES_SAMPLER_ARG must be a valid number');
310-
}
296+
export function customBuildSamplerFromEnv(resource: Resource, useXraySampler: boolean = false): Sampler {
297+
if (useXraySampler || process.env.OTEL_TRACES_SAMPLER === 'xray') {
298+
const samplerArgumentEnv: string | undefined = process.env.OTEL_TRACES_SAMPLER_ARG;
299+
let endpoint: string | undefined = undefined;
300+
let pollingInterval: number | undefined = undefined;
301+
302+
if (samplerArgumentEnv !== undefined) {
303+
const args: string[] = samplerArgumentEnv.split(',');
304+
for (const arg of args) {
305+
const equalIndex: number = arg.indexOf('=');
306+
if (equalIndex === -1) {
307+
continue;
308+
}
309+
const keyValue: string[] = [arg.substring(0, equalIndex), arg.substring(equalIndex + 1)];
310+
if (keyValue[0] === 'endpoint') {
311+
endpoint = keyValue[1];
312+
} else if (keyValue[0] === 'polling_interval') {
313+
pollingInterval = Number(keyValue[1]);
314+
if (isNaN(pollingInterval)) {
315+
pollingInterval = undefined;
316+
diag.error('polling_interval in OTEL_TRACES_SAMPLER_ARG must be a valid number');
311317
}
312318
}
313319
}
314-
315-
diag.debug(`XRay Sampler Endpoint: ${endpoint}`);
316-
diag.debug(`XRay Sampler Polling Interval: ${pollingInterval}`);
317-
return new AwsXRayRemoteSampler({ resource: resource, endpoint: endpoint, pollingInterval: pollingInterval });
318320
}
321+
322+
diag.info('AWS XRay Sampler enabled');
323+
diag.debug(`XRay Sampler Endpoint: ${endpoint}`);
324+
diag.debug(`XRay Sampler Polling Interval: ${pollingInterval}`);
325+
return new AwsXRayRemoteSampler({ resource: resource, endpoint: endpoint, pollingInterval: pollingInterval });
319326
}
320327

321328
return buildSamplerFromEnv();

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-span-metrics-processor-builder.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Resource } from '@opentelemetry/resources';
66
import { AwsMetricAttributeGenerator } from './aws-metric-attribute-generator';
77
import { AwsSpanMetricsProcessor } from './aws-span-metrics-processor';
88
import { MetricAttributeGenerator } from './metric-attribute-generator';
9+
import { ForceFlushFunction } from './aws-span-processing-util';
910

1011
// Metric instrument configuration constants
1112
const ERROR: string = 'Error';
@@ -22,18 +23,24 @@ export class AwsSpanMetricsProcessorBuilder {
2223
// Required builder elements
2324
private meterProvider: MeterProvider;
2425
private resource: Resource;
26+
private forceFlushFunction: ForceFlushFunction;
2527

2628
// Optional builder elements
2729
private generator: MetricAttributeGenerator = AwsSpanMetricsProcessorBuilder.DEFAULT_GENERATOR;
2830
private scopeName: string = AwsSpanMetricsProcessorBuilder.DEFAULT_SCOPE_NAME;
2931

30-
public static create(meterProvider: MeterProvider, resource: Resource): AwsSpanMetricsProcessorBuilder {
31-
return new AwsSpanMetricsProcessorBuilder(meterProvider, resource);
32+
public static create(
33+
meterProvider: MeterProvider,
34+
resource: Resource,
35+
meterProviderForceFlusher: ForceFlushFunction
36+
): AwsSpanMetricsProcessorBuilder {
37+
return new AwsSpanMetricsProcessorBuilder(meterProvider, resource, meterProviderForceFlusher);
3238
}
3339

34-
private constructor(meterProvider: MeterProvider, resource: Resource) {
40+
private constructor(meterProvider: MeterProvider, resource: Resource, meterProviderForceFlusher: ForceFlushFunction) {
3541
this.meterProvider = meterProvider;
3642
this.resource = resource;
43+
this.forceFlushFunction = meterProviderForceFlusher;
3744
}
3845

3946
/**
@@ -72,7 +79,8 @@ export class AwsSpanMetricsProcessorBuilder {
7279
faultHistogram,
7380
latencyHistogram,
7481
this.generator,
75-
this.resource
82+
this.resource,
83+
this.forceFlushFunction
7684
);
7785
}
7886
}

0 commit comments

Comments
 (0)