Skip to content

Commit c6179db

Browse files
committed
Merge branch 'lambda-layer-E2E' of https://github.com/mxiamxia/aws-otel-js-instrumentation into lambda-layer-E2E
2 parents 96eec18 + 0e2ff64 commit c6179db

File tree

60 files changed

+3941
-132
lines changed

Some content is hidden

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

60 files changed

+3941
-132
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: 22 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,6 +37,27 @@
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": {
4162
"@aws-sdk/client-bedrock": "3.632.0",
4263
"@aws-sdk/client-bedrock-agent": "3.632.0",

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

Lines changed: 30 additions & 29 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);
@@ -291,37 +293,36 @@ export class AwsOpentelemetryConfigurator {
291293
}
292294
}
293295

294-
export function customBuildSamplerFromEnv(resource: Resource): Sampler {
295-
switch (process.env.OTEL_TRACES_SAMPLER) {
296-
case 'xray': {
297-
const samplerArgumentEnv: string | undefined = process.env.OTEL_TRACES_SAMPLER_ARG;
298-
let endpoint: string | undefined = undefined;
299-
let pollingInterval: number | undefined = undefined;
300-
301-
if (samplerArgumentEnv !== undefined) {
302-
const args: string[] = samplerArgumentEnv.split(',');
303-
for (const arg of args) {
304-
const equalIndex: number = arg.indexOf('=');
305-
if (equalIndex === -1) {
306-
continue;
307-
}
308-
const keyValue: string[] = [arg.substring(0, equalIndex), arg.substring(equalIndex + 1)];
309-
if (keyValue[0] === 'endpoint') {
310-
endpoint = keyValue[1];
311-
} else if (keyValue[0] === 'polling_interval') {
312-
pollingInterval = Number(keyValue[1]);
313-
if (isNaN(pollingInterval)) {
314-
pollingInterval = undefined;
315-
diag.error('polling_interval in OTEL_TRACES_SAMPLER_ARG must be a valid number');
316-
}
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');
317317
}
318318
}
319319
}
320-
321-
diag.debug(`XRay Sampler Endpoint: ${endpoint}`);
322-
diag.debug(`XRay Sampler Polling Interval: ${pollingInterval}`);
323-
return new AwsXRayRemoteSampler({ resource: resource, endpoint: endpoint, pollingInterval: pollingInterval });
324320
}
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 });
325326
}
326327

327328
return buildSamplerFromEnv();

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22
// SPDX-License-Identifier: Apache-2.0
33
// Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License.
44

5-
import { DiagConsoleLogger, diag, trace } from '@opentelemetry/api';
5+
// Short-term workaround to avoid Upsteam OTel emitting logs such as:
6+
// - `OTEL_TRACES_SAMPLER value "xray invalid, defaulting to always_on".`
7+
// OTel dependencies will always load a default Sampler configuration. Although unused, that
8+
// load process will read the `OTEL_TRACES_SAMPLER` value and may emit the above log, which is
9+
// unwanted for `xray` value. Thus we temporarily remove this env var to avoid the unwanted log.
10+
let useXraySampler = false;
11+
if (process.env.OTEL_TRACES_SAMPLER === 'xray') {
12+
delete process.env.OTEL_TRACES_SAMPLER;
13+
useXraySampler = true;
14+
}
15+
16+
import { diag, DiagConsoleLogger, trace } from '@opentelemetry/api';
617
import { getNodeAutoInstrumentations, InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
718
import { Instrumentation } from '@opentelemetry/instrumentation';
819
import * as opentelemetry from '@opentelemetry/sdk-node';
@@ -56,7 +67,7 @@ const instrumentations: Instrumentation[] = getNodeAutoInstrumentations(instrume
5667
// Apply instrumentation patches
5768
applyInstrumentationPatches(instrumentations);
5869

59-
const configurator: AwsOpentelemetryConfigurator = new AwsOpentelemetryConfigurator(instrumentations);
70+
const configurator: AwsOpentelemetryConfigurator = new AwsOpentelemetryConfigurator(instrumentations, useXraySampler);
6071
const configuration: Partial<opentelemetry.NodeSDKConfiguration> = configurator.configure();
6172

6273
const sdk: opentelemetry.NodeSDK = new opentelemetry.NodeSDK(configuration);
@@ -67,14 +78,15 @@ const sdk: opentelemetry.NodeSDK = new opentelemetry.NodeSDK(configuration);
6778
// we wish to make contributions to upstream to improve customizability of the Node auto-instrumentation.
6879
try {
6980
sdk.start();
81+
82+
diag.info('Setting TraceProvider for instrumentations at the end of initialization');
7083
for (const instrumentation of instrumentations) {
71-
diag.info('Set TraceProvider for instrumentations at the end of initialization');
7284
instrumentation.setTracerProvider(trace.getTracerProvider());
7385
}
7486

75-
diag.info('AWS Distro of OpenTelemetry automatic instrumentation started successfully');
7687
diag.debug(`Environment variable OTEL_PROPAGATORS is set to '${process.env.OTEL_PROPAGATORS}'`);
7788
diag.debug(`Environment variable OTEL_EXPORTER_OTLP_PROTOCOL is set to '${process.env.OTEL_EXPORTER_OTLP_PROTOCOL}'`);
89+
diag.info('AWS Distro of OpenTelemetry automatic instrumentation started successfully');
7890
} catch (error) {
7991
diag.error(
8092
'Error initializing AWS Distro of OpenTelemetry SDK. Your application is not instrumented and will not produce telemetry',
@@ -90,3 +102,8 @@ process.on('SIGTERM', () => {
90102
});
91103

92104
// END The OpenTelemetry Authors code
105+
106+
// Respect original `OTEL_TRACES_SAMPLER` as we previously deleted it temporarily for value `xray`
107+
if (useXraySampler) {
108+
process.env.OTEL_TRACES_SAMPLER = 'xray';
109+
}

aws-distro-opentelemetry-node-autoinstrumentation/src/sampler/aws-xray-remote-sampler.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,38 @@ const DEFAULT_RULES_POLLING_INTERVAL_SECONDS: number = 5 * 60;
2222
// Default endpoint for awsproxy : https://aws-otel.github.io/docs/getting-started/remote-sampling#enable-awsproxy-extension
2323
const DEFAULT_AWS_PROXY_ENDPOINT: string = 'http://localhost:2000';
2424

25+
// Wrapper class to ensure that all XRay Sampler Functionality in _AwsXRayRemoteSampler
26+
// uses ParentBased logic to respect the parent span's sampling decision
2527
export class AwsXRayRemoteSampler implements Sampler {
28+
private _root: ParentBasedSampler;
29+
constructor(samplerConfig: AwsXRayRemoteSamplerConfig) {
30+
this._root = new ParentBasedSampler({ root: new _AwsXRayRemoteSampler(samplerConfig) });
31+
}
32+
public shouldSample(
33+
context: Context,
34+
traceId: string,
35+
spanName: string,
36+
spanKind: SpanKind,
37+
attributes: Attributes,
38+
links: Link[]
39+
): SamplingResult {
40+
return this._root.shouldSample(context, traceId, spanName, spanKind, attributes, links);
41+
}
42+
43+
public toString(): string {
44+
return `AwsXRayRemoteSampler{root=${this._root.toString()}`;
45+
}
46+
}
47+
48+
// _AwsXRayRemoteSampler contains all core XRay Sampler Functionality,
49+
// however it is NOT Parent-based (e.g. Sample logic runs for each span)
50+
// Not intended for external use, use Parent-based `AwsXRayRemoteSampler` instead.
51+
export class _AwsXRayRemoteSampler implements Sampler {
2652
private rulePollingIntervalMillis: number;
2753
private targetPollingInterval: number;
2854
private awsProxyEndpoint: string;
2955
private ruleCache: RuleCache;
30-
private fallbackSampler: ParentBasedSampler;
56+
private fallbackSampler: FallbackSampler;
3157
private samplerDiag: DiagLogger;
3258
private rulePoller: NodeJS.Timer | undefined;
3359
private targetPoller: NodeJS.Timer | undefined;
@@ -53,8 +79,8 @@ export class AwsXRayRemoteSampler implements Sampler {
5379
this.targetPollingJitterMillis = (Math.random() / 10) * 1000;
5480

5581
this.awsProxyEndpoint = samplerConfig.endpoint ? samplerConfig.endpoint : DEFAULT_AWS_PROXY_ENDPOINT;
56-
this.fallbackSampler = new ParentBasedSampler({ root: new FallbackSampler() });
57-
this.clientId = AwsXRayRemoteSampler.generateClientId();
82+
this.fallbackSampler = new FallbackSampler();
83+
this.clientId = _AwsXRayRemoteSampler.generateClientId();
5884
this.ruleCache = new RuleCache(samplerConfig.resource);
5985

6086
this.samplingClient = new AwsXraySamplingClient(this.awsProxyEndpoint, this.samplerDiag);
@@ -95,7 +121,9 @@ export class AwsXRayRemoteSampler implements Sampler {
95121
}
96122

97123
public toString(): string {
98-
return 'AwsXRayRemoteSampler{remote sampling with AWS X-Ray}';
124+
return `_AwsXRayRemoteSampler{awsProxyEndpoint=${
125+
this.awsProxyEndpoint
126+
}, rulePollingIntervalMillis=${this.rulePollingIntervalMillis.toString()}}`;
99127
}
100128

101129
private startSamplingRulesPoller(): void {

aws-distro-opentelemetry-node-autoinstrumentation/src/sampler/sampling-rule-applier.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,7 @@
33

44
import { AttributeValue, Attributes, Context, Link, SpanKind } from '@opentelemetry/api';
55
import { Resource } from '@opentelemetry/resources';
6-
import {
7-
ParentBasedSampler,
8-
SamplingDecision,
9-
SamplingResult,
10-
TraceIdRatioBasedSampler,
11-
} from '@opentelemetry/sdk-trace-base';
6+
import { SamplingDecision, SamplingResult, TraceIdRatioBasedSampler } from '@opentelemetry/sdk-trace-base';
127
import {
138
ATTR_CLIENT_ADDRESS,
149
ATTR_HTTP_REQUEST_METHOD,
@@ -39,20 +34,20 @@ const MAX_DATE_TIME_MILLIS: number = new Date(8_640_000_000_000_000).getTime();
3934

4035
export class SamplingRuleApplier {
4136
public samplingRule: SamplingRule;
42-
private reservoirSampler: ParentBasedSampler;
43-
private fixedRateSampler: ParentBasedSampler;
37+
private reservoirSampler: RateLimitingSampler;
38+
private fixedRateSampler: TraceIdRatioBasedSampler;
4439
private statistics: Statistics;
4540
private borrowingEnabled: boolean;
4641
private reservoirExpiryTimeInMillis: number;
4742

4843
constructor(samplingRule: ISamplingRule, statistics: Statistics = new Statistics(), target?: SamplingTargetDocument) {
4944
this.samplingRule = new SamplingRule(samplingRule);
5045

51-
this.fixedRateSampler = new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(this.samplingRule.FixedRate) });
46+
this.fixedRateSampler = new TraceIdRatioBasedSampler(this.samplingRule.FixedRate);
5247
if (samplingRule.ReservoirSize > 0) {
53-
this.reservoirSampler = new ParentBasedSampler({ root: new RateLimitingSampler(1) });
48+
this.reservoirSampler = new RateLimitingSampler(1);
5449
} else {
55-
this.reservoirSampler = new ParentBasedSampler({ root: new RateLimitingSampler(0) });
50+
this.reservoirSampler = new RateLimitingSampler(0);
5651
}
5752

5853
this.reservoirExpiryTimeInMillis = MAX_DATE_TIME_MILLIS;
@@ -63,7 +58,7 @@ export class SamplingRuleApplier {
6358
if (target) {
6459
this.borrowingEnabled = false;
6560
if (target.ReservoirQuota) {
66-
this.reservoirSampler = new ParentBasedSampler({ root: new RateLimitingSampler(target.ReservoirQuota) });
61+
this.reservoirSampler = new RateLimitingSampler(target.ReservoirQuota);
6762
}
6863

6964
if (target.ReservoirQuotaTTL) {
@@ -73,7 +68,7 @@ export class SamplingRuleApplier {
7368
}
7469

7570
if (target.FixedRate) {
76-
this.fixedRateSampler = new ParentBasedSampler({ root: new TraceIdRatioBasedSampler(target.FixedRate) });
71+
this.fixedRateSampler = new TraceIdRatioBasedSampler(target.FixedRate);
7772
}
7873
}
7974
}
@@ -156,7 +151,7 @@ export class SamplingRuleApplier {
156151
}
157152

158153
if (result.decision === SamplingDecision.NOT_RECORD) {
159-
result = this.fixedRateSampler.shouldSample(context, traceId, spanName, spanKind, attributes, links);
154+
result = this.fixedRateSampler.shouldSample(context, traceId);
160155
}
161156

162157
this.statistics.SampleCount += result.decision !== SamplingDecision.NOT_RECORD ? 1 : 0;

0 commit comments

Comments
 (0)