Skip to content

Commit 208fd2f

Browse files
committed
remove extended-instrumentations, use patching instead
1 parent f155a20 commit 208fd2f

File tree

7 files changed

+128
-323
lines changed

7 files changed

+128
-323
lines changed

aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-lambda.ts

Lines changed: 0 additions & 44 deletions
This file was deleted.

aws-distro-opentelemetry-node-autoinstrumentation/src/patches/extended-instrumentations/aws-sdk-instrumentation-extended.ts

Lines changed: 0 additions & 42 deletions
This file was deleted.

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

Lines changed: 86 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,12 @@ import {
1212
trace,
1313
Span,
1414
Tracer,
15+
SpanStatusCode,
16+
defaultTextMapSetter,
1517
} from '@opentelemetry/api';
1618
import { Instrumentation } from '@opentelemetry/instrumentation';
1719
import {
20+
AwsInstrumentation,
1821
AwsSdkInstrumentationConfig,
1922
NormalizedRequest,
2023
NormalizedResponse,
@@ -31,9 +34,8 @@ import {
3134
} from './aws/services/bedrock';
3235
import { SecretsManagerServiceExtension } from './aws/services/secretsmanager';
3336
import { StepFunctionsServiceExtension } from './aws/services/step-functions';
34-
import { InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
35-
import { AwsSdkInstrumentationExtended } from './extended-instrumentations/aws-sdk-instrumentation-extended';
36-
import { AwsLambdaInstrumentationPatch } from './extended-instrumentations/aws-lambda';
37+
import { AwsLambdaInstrumentation } from '@opentelemetry/instrumentation-aws-lambda';
38+
import type { Command as AwsV3Command } from '@aws-sdk/types';
3739

3840
export const traceContextEnvironmentKey = '_X_AMZN_TRACE_ID';
3941
const awsPropagator = new AWSXRayPropagator();
@@ -46,11 +48,7 @@ export const headerGetter: TextMapGetter<APIGatewayProxyEventHeaders> = {
4648
},
4749
};
4850

49-
export function applyInstrumentationPatches(
50-
instrumentations: Instrumentation[],
51-
instrumentationConfigs?: InstrumentationConfigMap,
52-
usePatchedAwsLambdaInstrumentation: boolean = false
53-
): void {
51+
export function applyInstrumentationPatches(instrumentations: Instrumentation[]): void {
5452
/*
5553
Apply patches to upstream instrumentation libraries.
5654
@@ -62,10 +60,8 @@ export function applyInstrumentationPatches(
6260
*/
6361
instrumentations.forEach((instrumentation, index) => {
6462
if (instrumentation.instrumentationName === '@opentelemetry/instrumentation-aws-sdk') {
65-
diag.debug('Overriding aws sdk instrumentation');
66-
instrumentations[index] = new AwsSdkInstrumentationExtended(
67-
instrumentationConfigs ? instrumentationConfigs['@opentelemetry/instrumentation-aws-sdk'] : undefined
68-
);
63+
diag.debug('Patching aws sdk instrumentation');
64+
patchAwsSdkInstrumentation(instrumentation);
6965

7066
// Access private property servicesExtensions of AwsInstrumentation
7167
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -83,18 +79,11 @@ export function applyInstrumentationPatches(
8379
patchSnsServiceExtension(services.get('SNS'));
8480
patchLambdaServiceExtension(services.get('Lambda'));
8581
}
82+
} else if (instrumentation.instrumentationName === '@opentelemetry/instrumentation-aws-lambda') {
83+
diag.debug('Patching aws lambda instrumentation');
84+
patchAwsLambdaInstrumentation(instrumentation);
8685
}
8786
});
88-
89-
if (usePatchedAwsLambdaInstrumentation) {
90-
diag.debug('Overriding aws lambda instrumentation');
91-
const lambdaInstrumentation = new AwsLambdaInstrumentationPatch({
92-
// When the `AwsLambdaInstrumentationPatch` is removed in favor of the patched one,
93-
// setting `eventContextExtractor` should be in the `instrumentationConfigs` map.
94-
eventContextExtractor: customExtractor,
95-
});
96-
instrumentations.push(lambdaInstrumentation);
97-
}
9887
}
9988

10089
/*
@@ -259,3 +248,78 @@ function patchLambdaServiceExtension(lambdaServiceExtension: any): void {
259248
}
260249
}
261250
}
251+
252+
// Override the upstream private _endSpan method to remove the unnecessary metric force-flush error message
253+
// https://github.com/open-telemetry/opentelemetry-js-contrib/blob/main/plugins/node/opentelemetry-instrumentation-aws-lambda/src/instrumentation.ts#L358-L398
254+
function patchAwsLambdaInstrumentation(instrumentation: Instrumentation): void {
255+
if (instrumentation) {
256+
(instrumentation as AwsLambdaInstrumentation)['_endSpan'] = function (
257+
span: Span,
258+
err: string | Error | null | undefined,
259+
callback: () => void
260+
) {
261+
if (err) {
262+
span.recordException(err);
263+
}
264+
265+
let errMessage;
266+
if (typeof err === 'string') {
267+
errMessage = err;
268+
} else if (err) {
269+
errMessage = err.message;
270+
}
271+
if (errMessage) {
272+
span.setStatus({
273+
code: SpanStatusCode.ERROR,
274+
message: errMessage,
275+
});
276+
}
277+
278+
span.end();
279+
280+
const flushers = [];
281+
if ((this as any)._traceForceFlusher) {
282+
flushers.push((this as any)._traceForceFlusher());
283+
} else {
284+
diag.error(
285+
'Spans may not be exported for the lambda function because we are not force flushing before callback.'
286+
);
287+
}
288+
289+
Promise.all(flushers).then(callback, callback);
290+
};
291+
}
292+
}
293+
294+
// Override the upstream private _getV3SmithyClientSendPatch method to add middleware to inject X-Ray Trace Context into HTTP Headers
295+
// 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
296+
const awsXrayPropagator = new AWSXRayPropagator();
297+
const V3_CLIENT_CONFIG_KEY = Symbol('opentelemetry.instrumentation.aws-sdk.client.config');
298+
type V3PluginCommand = AwsV3Command<any, any, any, any, any> & {
299+
[V3_CLIENT_CONFIG_KEY]?: any;
300+
};
301+
function patchAwsSdkInstrumentation(instrumentation: Instrumentation): void {
302+
if (instrumentation) {
303+
(instrumentation as AwsInstrumentation)['_getV3SmithyClientSendPatch'] = function (
304+
original: (...args: unknown[]) => Promise<any>
305+
) {
306+
return function send(this: any, command: V3PluginCommand, ...args: unknown[]): Promise<any> {
307+
this.middlewareStack?.add(
308+
(next: any, context: any) => async (middlewareArgs: any) => {
309+
awsXrayPropagator.inject(otelContext.active(), middlewareArgs.request.headers, defaultTextMapSetter);
310+
const result = await next(middlewareArgs);
311+
return result;
312+
},
313+
{
314+
step: 'build',
315+
name: '_adotInjectXrayContextMiddleware',
316+
override: true,
317+
}
318+
);
319+
320+
command[V3_CLIENT_CONFIG_KEY] = this.config;
321+
return original.apply(this, [command, ...args]);
322+
};
323+
};
324+
}
325+
}

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ import { diag, DiagConsoleLogger, trace } from '@opentelemetry/api';
1717
import { getNodeAutoInstrumentations, InstrumentationConfigMap } from '@opentelemetry/auto-instrumentations-node';
1818
import { Instrumentation } from '@opentelemetry/instrumentation';
1919
import * as opentelemetry from '@opentelemetry/sdk-node';
20-
import { AwsOpentelemetryConfigurator, isLambdaEnvironment } from './aws-opentelemetry-configurator';
21-
import { applyInstrumentationPatches } from './patches/instrumentation-patch';
20+
import { AwsOpentelemetryConfigurator } from './aws-opentelemetry-configurator';
21+
import { applyInstrumentationPatches, customExtractor } from './patches/instrumentation-patch';
2222

2323
diag.setLogger(new DiagConsoleLogger(), opentelemetry.core.getEnv().OTEL_LOG_LEVEL);
2424

@@ -36,8 +36,6 @@ This file may also be used to apply patches to upstream instrumentation - usuall
3636
long-term changes to upstream.
3737
*/
3838

39-
let usePatchedAwsLambdaInstrumentation = false;
40-
4139
export function setAwsDefaultEnvironmentVariables(): void {
4240
if (!process.env.OTEL_EXPORTER_OTLP_PROTOCOL) {
4341
process.env.OTEL_EXPORTER_OTLP_PROTOCOL = 'http/protobuf';
@@ -51,18 +49,13 @@ export function setAwsDefaultEnvironmentVariables(): void {
5149
if (!process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS) {
5250
process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS = 'fs,dns';
5351
}
54-
55-
// Temporary solution to never initialize the unpatched AWS Lambda Instrumentation,
56-
// and only initialize the patched one in `src/patches/extended-instrumentations/aws-lambda.ts`
57-
// This workaround is for until the patched Lambda Instrumentation is removed
58-
if (!process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS.includes('aws-lambda') && isLambdaEnvironment()) {
59-
process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS += ',aws-lambda';
60-
usePatchedAwsLambdaInstrumentation = true;
61-
}
6252
}
6353
setAwsDefaultEnvironmentVariables();
6454

65-
const instrumentationConfigs: InstrumentationConfigMap = {
55+
export const instrumentationConfigs: InstrumentationConfigMap = {
56+
'@opentelemetry/instrumentation-aws-lambda': {
57+
eventContextExtractor: customExtractor,
58+
},
6659
'@opentelemetry/instrumentation-aws-sdk': {
6760
suppressInternalInstrumentation: true,
6861
},
@@ -73,7 +66,7 @@ const instrumentationConfigs: InstrumentationConfigMap = {
7366
const instrumentations: Instrumentation[] = getNodeAutoInstrumentations(instrumentationConfigs);
7467

7568
// Apply instrumentation patches
76-
applyInstrumentationPatches(instrumentations, instrumentationConfigs, usePatchedAwsLambdaInstrumentation);
69+
applyInstrumentationPatches(instrumentations);
7770

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

0 commit comments

Comments
 (0)