Skip to content

Commit 461e0bb

Browse files
committed
merge conflict fix
2 parents 35785a1 + 03aa3b5 commit 461e0bb

File tree

9 files changed

+176
-7
lines changed

9 files changed

+176
-7
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
build
22
node_modules
33
.eslintrc.js
4-
version.ts
4+
version.ts
5+
src/third-party

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

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,18 @@
3131
"prepublishOnly": "npm run compile",
3232
"tdd": "yarn test -- --watch-extensions ts --watch",
3333
"test": "nyc ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
34-
"test:coverage": "nyc --all --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
34+
"test:coverage": "nyc --check-coverage --functions 95 --lines 95 ts-mocha --timeout 10000 -p tsconfig.json --require '@opentelemetry/contrib-test-utils' 'test/**/*.ts'",
3535
"watch": "tsc -w"
3636
},
37+
"nyc": {
38+
"all": true,
39+
"include": [
40+
"src/**/*.ts"
41+
],
42+
"exclude": [
43+
"src/third-party/**/*.ts"
44+
]
45+
},
3746
"bugs": {
3847
"url": "https://github.com/aws-observability/aws-otel-js-instrumentation/issues"
3948
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk';
5+
import { context as otelContext, defaultTextMapSetter } from '@opentelemetry/api';
6+
import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray';
7+
import type { Command as AwsV3Command } from '@aws-sdk/types';
8+
9+
const awsXrayPropagator = new AWSXRayPropagator();
10+
const V3_CLIENT_CONFIG_KEY = Symbol('opentelemetry.instrumentation.aws-sdk.client.config');
11+
type V3PluginCommand = AwsV3Command<any, any, any, any, any> & {
12+
[V3_CLIENT_CONFIG_KEY]?: any;
13+
};
14+
15+
// This class extends the upstream AwsInstrumentation to override its patching mechanism of the `send` method.
16+
// The overriden method will additionally update the AWS SDK middleware stack to inject the `X-Amzn-Trace-Id` HTTP header.
17+
//
18+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
19+
// @ts-ignore
20+
export class AwsSdkInstrumentationExtended extends AwsInstrumentation {
21+
// Override the upstream private _getV3SmithyClientSendPatch method to add middleware to inject X-Ray Trace Context into HTTP Headers
22+
// https://github.com/open-telemetry/opentelemetry-js-contrib/blob/instrumentation-aws-sdk-v0.48.0/plugins/node/opentelemetry-instrumentation-aws-sdk/src/aws-sdk.ts#L373-L384
23+
override _getV3SmithyClientSendPatch(original: (...args: unknown[]) => Promise<any>) {
24+
return function send(this: any, command: V3PluginCommand, ...args: unknown[]): Promise<any> {
25+
this.middlewareStack?.add(
26+
(next: any, context: any) => async (middlewareArgs: any) => {
27+
awsXrayPropagator.inject(otelContext.active(), middlewareArgs.request.headers, defaultTextMapSetter);
28+
const result = await next(middlewareArgs);
29+
return result;
30+
},
31+
{
32+
step: 'build',
33+
name: '_adotInjectXrayContextMiddleware',
34+
override: true,
35+
}
36+
);
37+
38+
command[V3_CLIENT_CONFIG_KEY] = this.config;
39+
return original.apply(this, [command, ...args]);
40+
};
41+
}
42+
}

aws-distro-opentelemetry-node-autoinstrumentation/src/patches/instrumentation-patch.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@ import {
2525
} from './aws/services/bedrock';
2626
import { KinesisServiceExtension } from './aws/services/kinesis';
2727
import { S3ServiceExtension } from './aws/services/s3';
28-
import { AwsLambdaInstrumentationPatch } from './aws/services/aws-lambda';
2928
import { SecretsManagerServiceExtension } from './aws/services/secretsmanager';
3029
import { StepFunctionsServiceExtension } from './aws/services/step-functions';
30+
import { InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
31+
import { AwsSdkInstrumentationExtended } from './extended-instrumentations/aws-sdk-instrumentation-extended';
32+
import { AwsLambdaInstrumentationPatch } from './extended-instrumentations/aws-lambda';
3133

3234
export const traceContextEnvironmentKey = '_X_AMZN_TRACE_ID';
3335
const awsPropagator = new AWSXRayPropagator();
@@ -40,7 +42,10 @@ export const headerGetter: TextMapGetter<APIGatewayProxyEventHeaders> = {
4042
},
4143
};
4244

43-
export function applyInstrumentationPatches(instrumentations: Instrumentation[]): void {
45+
export function applyInstrumentationPatches(
46+
instrumentations: Instrumentation[],
47+
instrumentationConfigs?: InstrumentationConfigMap
48+
): void {
4449
/*
4550
Apply patches to upstream instrumentation libraries.
4651
@@ -52,10 +57,16 @@ export function applyInstrumentationPatches(instrumentations: Instrumentation[])
5257
*/
5358
instrumentations.forEach((instrumentation, index) => {
5459
if (instrumentation.instrumentationName === '@opentelemetry/instrumentation-aws-sdk') {
60+
diag.debug('Overriding aws sdk instrumentation');
61+
instrumentations[index] = new AwsSdkInstrumentationExtended(
62+
instrumentationConfigs ? instrumentationConfigs['@opentelemetry/instrumentation-aws-sdk'] : undefined
63+
);
64+
5565
// Access private property servicesExtensions of AwsInstrumentation
5666
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
5767
// @ts-ignore
58-
const services: Map<string, ServiceExtension> | undefined = (instrumentation as any).servicesExtensions?.services;
68+
const services: Map<string, ServiceExtension> | undefined = (instrumentations[index] as any).servicesExtensions
69+
?.services;
5970
if (services) {
6071
services.set('S3', new S3ServiceExtension());
6172
services.set('Kinesis', new KinesisServiceExtension());

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ const instrumentationConfigs: InstrumentationConfigMap = {
6262
const instrumentations: Instrumentation[] = getNodeAutoInstrumentations(instrumentationConfigs);
6363

6464
// Apply instrumentation patches
65-
applyInstrumentationPatches(instrumentations);
65+
applyInstrumentationPatches(instrumentations, instrumentationConfigs);
6666

6767
const configurator: AwsOpentelemetryConfigurator = new AwsOpentelemetryConfigurator(instrumentations, useXraySampler);
6868
const configuration: Partial<opentelemetry.NodeSDKConfiguration> = configurator.configure();

aws-distro-opentelemetry-node-autoinstrumentation/test/patches/aws/services/aws-lambda.test.ts renamed to aws-distro-opentelemetry-node-autoinstrumentation/test/patches/extended-instrumentations/aws-lambda.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as path from 'path';
66
import * as fs from 'fs';
77
import { diag } from '@opentelemetry/api';
88
import { InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation';
9-
import { AwsLambdaInstrumentationPatch } from '../../../../src/patches/aws/services/aws-lambda';
9+
import { AwsLambdaInstrumentationPatch } from '../../../src/patches/extended-instrumentations/aws-lambda';
1010

1111
describe('AwsLambdaInstrumentationPatch', () => {
1212
let instrumentation: AwsLambdaInstrumentationPatch;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import * as sinon from 'sinon';
4+
import { AwsSdkInstrumentationExtended } from '../../../src/patches/extended-instrumentations/aws-sdk-instrumentation-extended';
5+
import expect from 'expect';
6+
import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray';
7+
import { Context, TextMapSetter } from '@opentelemetry/api';
8+
9+
describe('AwsSdkInstrumentationExtended', () => {
10+
let instrumentation: AwsSdkInstrumentationExtended;
11+
12+
beforeEach(() => {
13+
instrumentation = new AwsSdkInstrumentationExtended({});
14+
});
15+
16+
afterEach(() => {
17+
sinon.restore();
18+
});
19+
20+
it('overridden _getV3SmithyClientSendPatch updates MiddlewareStack', async () => {
21+
const mockedMiddlewareStackInternal: any = [];
22+
const mockedMiddlewareStack = {
23+
add: (arg1: any, arg2: any) => mockedMiddlewareStackInternal.push([arg1, arg2]),
24+
};
25+
const send = instrumentation
26+
._getV3SmithyClientSendPatch((...args: unknown[]) => Promise.resolve())
27+
.bind({ middlewareStack: mockedMiddlewareStack });
28+
sinon
29+
.stub(AWSXRayPropagator.prototype, 'inject')
30+
.callsFake((context: Context, carrier: unknown, setter: TextMapSetter) => {
31+
(carrier as any)['isCarrierModified'] = 'carrierIsModified';
32+
});
33+
34+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
35+
// @ts-ignore
36+
await send({}, null);
37+
38+
const middlewareArgs: any = {
39+
request: {
40+
headers: {},
41+
},
42+
};
43+
await mockedMiddlewareStackInternal[0][0]((arg: any) => Promise.resolve(), null)(middlewareArgs);
44+
45+
expect(middlewareArgs.request.headers['isCarrierModified']).toEqual('carrierIsModified');
46+
expect(mockedMiddlewareStackInternal[0][1].name).toEqual('_adotInjectXrayContextMiddleware');
47+
});
48+
});

aws-distro-opentelemetry-node-autoinstrumentation/test/patches/instrumentation-patch.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ import * as sinon from 'sinon';
2323
import { AWSXRAY_TRACE_ID_HEADER, AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray';
2424
import { Context } from 'aws-lambda';
2525
import { SinonStub } from 'sinon';
26+
import { S3 } from '@aws-sdk/client-s3';
27+
import nock = require('nock');
28+
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
29+
import { getTestSpans, registerInstrumentationTesting } from '@opentelemetry/contrib-test-utils';
30+
import { AwsSdkInstrumentationExtended } from '../../src/patches/extended-instrumentations/aws-sdk-instrumentation-extended';
2631

2732
const _STREAM_NAME: string = 'streamName';
2833
const _BUCKET_NAME: string = 'bucketName';
@@ -51,6 +56,10 @@ const UNPATCHED_INSTRUMENTATIONS: Instrumentation[] = getNodeAutoInstrumentation
5156
const PATCHED_INSTRUMENTATIONS: Instrumentation[] = getNodeAutoInstrumentations();
5257
applyInstrumentationPatches(PATCHED_INSTRUMENTATIONS);
5358

59+
const extendedAwsSdkInstrumentation: AwsInstrumentation = new AwsInstrumentation();
60+
applyInstrumentationPatches([extendedAwsSdkInstrumentation]);
61+
registerInstrumentationTesting(extendedAwsSdkInstrumentation);
62+
5463
describe('InstrumentationPatchTest', () => {
5564
it('SanityTestUnpatchedAwsSdkInstrumentation', () => {
5665
const awsSdkInstrumentation: AwsInstrumentation = extractAwsSdkInstrumentation(UNPATCHED_INSTRUMENTATIONS);
@@ -109,6 +118,9 @@ describe('InstrumentationPatchTest', () => {
109118
expect(services.get('BedrockRuntime')).toBeTruthy();
110119
// Sanity check
111120
expect(services.has('InvalidService')).toBeFalsy();
121+
122+
// Check that the original AWS SDK Instrumentation is replaced with the extended version
123+
expect(awsSdkInstrumentation).toBeInstanceOf(AwsSdkInstrumentationExtended);
112124
});
113125

114126
it('S3 without patching', () => {
@@ -549,6 +561,52 @@ describe('InstrumentationPatchTest', () => {
549561
expect(filteredInstrumentations.length).toEqual(1);
550562
return filteredInstrumentations[0] as AwsLambdaInstrumentation;
551563
}
564+
565+
describe('AwsSdkInstrumentationPatchTest', () => {
566+
let s3: S3;
567+
const region = 'us-east-1';
568+
569+
it('injects trace context header into request via propagator', async () => {
570+
s3 = new S3({
571+
region: region,
572+
credentials: {
573+
accessKeyId: 'abcde',
574+
secretAccessKey: 'abcde',
575+
},
576+
});
577+
578+
const dummyBucketName: string = 'dummy-bucket-name';
579+
let reqHeaders: any = {};
580+
581+
nock(`https://${dummyBucketName}.s3.${region}.amazonaws.com`)
582+
.get('/')
583+
.reply(200, function (uri: any, requestBody: any) {
584+
reqHeaders = this.req.headers;
585+
return 'null';
586+
});
587+
588+
await s3
589+
.listObjects({
590+
Bucket: dummyBucketName,
591+
})
592+
.catch((err: any) => {});
593+
594+
const testSpans: ReadableSpan[] = getTestSpans();
595+
const listObjectsSpans: ReadableSpan[] = testSpans.filter((s: ReadableSpan) => {
596+
return s.name === 'S3.ListObjects';
597+
});
598+
599+
expect(listObjectsSpans.length).toBe(1);
600+
601+
const traceId = listObjectsSpans[0].spanContext().traceId;
602+
const spanId = listObjectsSpans[0].spanContext().spanId;
603+
expect(reqHeaders['x-amzn-trace-id'] as string).toEqual(
604+
`Root=1-${traceId.substring(0, 8)}-${listObjectsSpans[0]
605+
.spanContext()
606+
.traceId.substring(8, 32)};Parent=${spanId};Sampled=1`
607+
);
608+
});
609+
});
552610
});
553611

554612
describe('customExtractor', () => {

0 commit comments

Comments
 (0)