Skip to content

Commit 0e2ff64

Browse files
authored
Merge branch 'main' into lambda-layer-E2E
2 parents 6e2dadf + d3b5825 commit 0e2ff64

File tree

71 files changed

+5040
-181
lines changed

Some content is hidden

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

71 files changed

+5040
-181
lines changed

.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

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
}

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

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Resource } from '@opentelemetry/resources';
66
import { ReadableSpan, Span, SpanProcessor } from '@opentelemetry/sdk-trace-base';
77
import { SEMATTRS_HTTP_STATUS_CODE } from '@opentelemetry/semantic-conventions';
88
import { AttributeMap, MetricAttributeGenerator } from './metric-attribute-generator';
9+
import { ForceFlushFunction } from './aws-span-processing-util';
910

1011
/**
1112
* This processor will generate metrics based on span data. It depends on a
@@ -39,30 +40,41 @@ export class AwsSpanMetricsProcessor implements SpanProcessor {
3940

4041
private generator: MetricAttributeGenerator;
4142
private resource: Resource;
43+
private forceFlushFunction: ForceFlushFunction;
4244

4345
/** Use {@link AwsSpanMetricsProcessorBuilder} to construct this processor. */
4446
static create(
4547
errorHistogram: Histogram,
4648
faultHistogram: Histogram,
4749
latencyHistogram: Histogram,
4850
generator: MetricAttributeGenerator,
49-
resource: Resource
51+
resource: Resource,
52+
forceFlushFunction: ForceFlushFunction
5053
): AwsSpanMetricsProcessor {
51-
return new AwsSpanMetricsProcessor(errorHistogram, faultHistogram, latencyHistogram, generator, resource);
54+
return new AwsSpanMetricsProcessor(
55+
errorHistogram,
56+
faultHistogram,
57+
latencyHistogram,
58+
generator,
59+
resource,
60+
forceFlushFunction
61+
);
5262
}
5363

5464
private constructor(
5565
errorHistogram: Histogram,
5666
faultHistogram: Histogram,
5767
latencyHistogram: Histogram,
5868
generator: MetricAttributeGenerator,
59-
resource: Resource
69+
resource: Resource,
70+
forceFlushFunction: ForceFlushFunction
6071
) {
6172
this.errorHistogram = errorHistogram;
6273
this.faultHistogram = faultHistogram;
6374
this.latencyHistogram = latencyHistogram;
6475
this.generator = generator;
6576
this.resource = resource;
77+
this.forceFlushFunction = forceFlushFunction;
6678
}
6779

6880
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -129,6 +141,6 @@ export class AwsSpanMetricsProcessor implements SpanProcessor {
129141
}
130142

131143
public forceFlush(): Promise<void> {
132-
return Promise.resolve();
144+
return this.forceFlushFunction();
133145
}
134146
}

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-span-processing-util.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { AWS_ATTRIBUTE_KEYS } from './aws-attribute-keys';
2020
import { AWS_LAMBDA_FUNCTION_NAME_CONFIG, isLambdaEnvironment } from './aws-opentelemetry-configurator';
2121
import * as SQL_DIALECT_KEYWORDS_JSON from './configuration/sql_dialect_keywords.json';
2222

23+
export type ForceFlushFunction = (options?: any) => Promise<void>;
24+
2325
/** Utility class designed to support shared logic across AWS Span Processors. */
2426
export class AwsSpanProcessingUtil {
2527
// Default attribute values if no valid span attribute value is identified
@@ -38,6 +40,10 @@ export class AwsSpanProcessingUtil {
3840
static MAX_KEYWORD_LENGTH: number = 27;
3941
static SQL_DIALECT_PATTERN: string = '^(?:' + AwsSpanProcessingUtil.getDialectKeywords().join('|') + ')\\b';
4042

43+
// TODO: Use Semantic Conventions once upgraded
44+
static GEN_AI_REQUEST_MODEL: string = 'gen_ai.request.model';
45+
static GEN_AI_SYSTEM: string = 'gen_ai.system';
46+
4147
static getDialectKeywords(): string[] {
4248
return SQL_DIALECT_KEYWORDS_JSON.keywords;
4349
}

0 commit comments

Comments
 (0)