Skip to content

Commit bcaca08

Browse files
authored
chore(aws): Remove manual span creation (#17310)
the TODO says this is _likely_ not needed anymore since OTel correctly wraps the handler in all cases (I never ran into any situation where it didn't). Also, when a user would use `Sentry.wrapHandler` manually, the detection mechanism checking whether the handler was already wrapped by OTel would not work anyway, since it that case OTel wraps it only *after* we did, resulting in double spans. Also also, removing this makes our lives easier for streaming, where this resulted in some issues with overlapping spans.
1 parent 190e068 commit bcaca08

File tree

3 files changed

+34
-212
lines changed

3 files changed

+34
-212
lines changed

dev-packages/e2e-tests/test-applications/aws-serverless-esm/tests/basic.test.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ test('AWS Serverless SDK sends events in ESM mode', async ({ request }) => {
4343
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
4444
});
4545

46-
expect(transactionEvent.spans).toHaveLength(3);
46+
expect(transactionEvent.spans).toHaveLength(2);
4747

4848
// shows that the Otel Http instrumentation is working
4949
expect(transactionEvent.spans).toContainEqual(
@@ -58,19 +58,6 @@ test('AWS Serverless SDK sends events in ESM mode', async ({ request }) => {
5858
}),
5959
);
6060

61-
expect(transactionEvent.spans).toContainEqual(
62-
expect.objectContaining({
63-
data: {
64-
'sentry.op': 'function.aws.lambda',
65-
'sentry.origin': 'auto.function.serverless',
66-
'sentry.source': 'component',
67-
},
68-
description: 'my-lambda',
69-
op: 'function.aws.lambda',
70-
origin: 'auto.function.serverless',
71-
}),
72-
);
73-
7461
// shows that the manual span creation is working
7562
expect(transactionEvent.spans).toContainEqual(
7663
expect.objectContaining({

packages/aws-serverless/src/sdk.ts

Lines changed: 21 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,24 @@
1-
import type { Integration, Options, Scope, Span } from '@sentry/core';
2-
import {
3-
applySdkMetadata,
4-
debug,
5-
getSDKSource,
6-
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
7-
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
8-
} from '@sentry/core';
1+
import type { Integration, Options, Scope } from '@sentry/core';
2+
import { applySdkMetadata, consoleSandbox, debug, getSDKSource } from '@sentry/core';
93
import type { NodeClient, NodeOptions } from '@sentry/node';
104
import {
115
captureException,
126
captureMessage,
13-
continueTrace,
147
flush,
158
getCurrentScope,
169
getDefaultIntegrationsWithoutPerformance,
1710
initWithoutDefaultIntegrations,
18-
startSpanManual,
1911
withScope,
2012
} from '@sentry/node';
2113
import type { Context, Handler } from 'aws-lambda';
2214
import { existsSync } from 'fs';
23-
import { hostname } from 'os';
2415
import { basename, resolve } from 'path';
2516
import { performance } from 'perf_hooks';
2617
import { types } from 'util';
2718
import { DEBUG_BUILD } from './debug-build';
2819
import { awsIntegration } from './integration/aws';
2920
import { awsLambdaIntegration } from './integration/awslambda';
30-
import { getAwsTraceData, markEventUnhandled } from './utils';
21+
import { markEventUnhandled } from './utils';
3122

3223
const { isPromise } = types;
3324

@@ -54,10 +45,10 @@ export interface WrapperOptions {
5445
* @default false
5546
*/
5647
captureAllSettledReasons: boolean;
48+
// TODO(v11): Remove this option since its no longer used.
5749
/**
58-
* Automatically trace all handler invocations.
59-
* You may want to disable this if you use express within Lambda.
60-
* @default true
50+
* @deprecated This option has no effect and will be removed in a future major version.
51+
* If you want to disable tracing, set `SENTRY_TRACES_SAMPLE_RATE` to `0.0`, otherwise OpenTelemetry will automatically trace the handler.
6152
*/
6253
startTrace: boolean;
6354
}
@@ -207,18 +198,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context, startTi
207198
});
208199
}
209200

210-
/**
211-
* Adds additional transaction-related information from the environment and AWS Context to the Sentry Scope.
212-
*
213-
* @param scope Scope that should be enhanced
214-
* @param context AWS Lambda context that will be used to extract some part of the data
215-
*/
216-
function enhanceScopeWithTransactionData(scope: Scope, context: Context): void {
217-
scope.setTransactionName(context.functionName);
218-
scope.setTag('server_name', process.env._AWS_XRAY_DAEMON_ADDRESS || process.env.SENTRY_NAME || hostname());
219-
scope.setTag('url', `awslambda:///${context.functionName}`);
220-
}
221-
222201
/**
223202
* Wraps a lambda handler adding it error capture and tracing capabilities.
224203
*
@@ -231,15 +210,27 @@ export function wrapHandler<TEvent, TResult>(
231210
wrapOptions: Partial<WrapperOptions> = {},
232211
): Handler<TEvent, TResult> {
233212
const START_TIME = performance.now();
213+
214+
// eslint-disable-next-line deprecation/deprecation
215+
if (typeof wrapOptions.startTrace !== 'undefined') {
216+
consoleSandbox(() => {
217+
// eslint-disable-next-line no-console
218+
console.warn(
219+
'The `startTrace` option is deprecated and will be removed in a future major version. If you want to disable tracing, set `SENTRY_TRACES_SAMPLE_RATE` to `0.0`.',
220+
);
221+
});
222+
}
223+
234224
const options: WrapperOptions = {
235225
flushTimeout: 2000,
236226
callbackWaitsForEmptyEventLoop: false,
237227
captureTimeoutWarning: true,
238228
timeoutWarningLimit: 500,
239229
captureAllSettledReasons: false,
240-
startTrace: true,
230+
startTrace: true, // TODO(v11): Remove this option. Set to true here to satisfy the type, but has no effect.
241231
...wrapOptions,
242232
};
233+
243234
let timeoutWarningTimer: NodeJS.Timeout;
244235

245236
// AWSLambda is like Express. It makes a distinction about handlers based on its last argument
@@ -293,7 +284,7 @@ export function wrapHandler<TEvent, TResult>(
293284
}, timeoutWarningDelay) as unknown as NodeJS.Timeout;
294285
}
295286

296-
async function processResult(span?: Span): Promise<TResult> {
287+
async function processResult(): Promise<TResult> {
297288
const scope = getCurrentScope();
298289

299290
let rv: TResult;
@@ -314,60 +305,15 @@ export function wrapHandler<TEvent, TResult>(
314305
throw e;
315306
} finally {
316307
clearTimeout(timeoutWarningTimer);
317-
if (span?.isRecording()) {
318-
span.end();
319-
}
320308
await flush(options.flushTimeout).catch(e => {
321309
DEBUG_BUILD && debug.error(e);
322310
});
323311
}
324312
return rv;
325313
}
326314

327-
// Only start a trace and root span if the handler is not already wrapped by Otel instrumentation
328-
// Otherwise, we create two root spans (one from otel, one from our wrapper).
329-
// If Otel instrumentation didn't work or was filtered by users, we still want to trace the handler.
330-
// TODO: Since bumping the OTEL Instrumentation, this is likely not needed anymore, we can possibly remove this (can be done whenever since it would be non-breaking)
331-
if (options.startTrace && !isWrappedByOtel(handler)) {
332-
const traceData = getAwsTraceData(event as { headers?: Record<string, string> }, context);
333-
334-
return continueTrace({ sentryTrace: traceData['sentry-trace'], baggage: traceData.baggage }, () => {
335-
return startSpanManual(
336-
{
337-
name: context.functionName,
338-
op: 'function.aws.lambda',
339-
attributes: {
340-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component',
341-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.serverless',
342-
},
343-
},
344-
span => {
345-
enhanceScopeWithTransactionData(getCurrentScope(), context);
346-
347-
return processResult(span);
348-
},
349-
);
350-
});
351-
}
352-
353315
return withScope(async () => {
354-
return processResult(undefined);
316+
return processResult();
355317
});
356318
};
357319
}
358-
359-
/**
360-
* Checks if Otel's AWSLambda instrumentation successfully wrapped the handler.
361-
* Check taken from @opentelemetry/core
362-
*/
363-
function isWrappedByOtel(
364-
// eslint-disable-next-line @typescript-eslint/ban-types
365-
handler: Function & { __original?: unknown; __unwrap?: unknown; __wrapped?: boolean },
366-
): boolean {
367-
return (
368-
typeof handler === 'function' &&
369-
typeof handler.__original === 'function' &&
370-
typeof handler.__unwrap === 'function' &&
371-
handler.__wrapped === true
372-
);
373-
}

0 commit comments

Comments
 (0)