Skip to content

Commit 8dd15ac

Browse files
committed
feat(serverlesss): allow disabling transaction traces
1 parent bb67a11 commit 8dd15ac

File tree

3 files changed

+76
-37
lines changed

3 files changed

+76
-37
lines changed

packages/serverless/src/awslambda.ts

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ export interface WrapperOptions {
4747
* @default false
4848
*/
4949
captureAllSettledReasons: boolean;
50+
/**
51+
* Automatically trace all handler invocations.
52+
* You may want to disable this if you use express within Lambda (use tracingHandler instead).
53+
* @default true
54+
*/
55+
startTransaction: boolean;
5056
}
5157

5258
export const defaultIntegrations: Integration[] = [...Sentry.defaultIntegrations, new AWSServices({ optional: true })];
@@ -178,11 +184,6 @@ function tryGetRemainingTimeInMillis(context: Context): number {
178184
* @param startTime performance.now() when wrapHandler was invoked
179185
*/
180186
function enhanceScopeWithEnvironmentData(scope: Scope, context: Context, startTime: number): void {
181-
scope.setTransactionName(context.functionName);
182-
183-
scope.setTag('server_name', process.env._AWS_XRAY_DAEMON_ADDRESS || process.env.SENTRY_NAME || hostname());
184-
scope.setTag('url', `awslambda:///${context.functionName}`);
185-
186187
scope.setContext('aws.lambda', {
187188
aws_request_id: context.awsRequestId,
188189
function_name: context.functionName,
@@ -204,6 +205,18 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context, startTi
204205
});
205206
}
206207

208+
/**
209+
* Adds additional transaction-related information from the environment and AWS Context to the Sentry Scope.
210+
*
211+
* @param scope Scope that should be enhanced
212+
* @param context AWS Lambda context that will be used to extract some part of the data
213+
*/
214+
function enhanceScopeWithTransactionData(scope: Scope, context: Context): void {
215+
scope.setTransactionName(context.functionName);
216+
scope.setTag('server_name', process.env._AWS_XRAY_DAEMON_ADDRESS || process.env.SENTRY_NAME || hostname());
217+
scope.setTag('url', `awslambda:///${context.functionName}`);
218+
}
219+
207220
/**
208221
* Wraps a lambda handler adding it error capture and tracing capabilities.
209222
*
@@ -222,6 +235,7 @@ export function wrapHandler<TEvent, TResult>(
222235
captureTimeoutWarning: true,
223236
timeoutWarningLimit: 500,
224237
captureAllSettledReasons: false,
238+
startTransaction: true,
225239
...wrapOptions,
226240
};
227241
let timeoutWarningTimer: NodeJS.Timeout;
@@ -279,36 +293,42 @@ export function wrapHandler<TEvent, TResult>(
279293

280294
const hub = getCurrentHub();
281295

282-
const eventWithHeaders = event as { headers?: { [key: string]: string } };
283-
284-
const sentryTrace =
285-
eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])
286-
? eventWithHeaders.headers['sentry-trace']
287-
: undefined;
288-
const baggage = eventWithHeaders.headers?.baggage;
289-
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
290-
sentryTrace,
291-
baggage,
292-
);
293-
hub.getScope().setPropagationContext(propagationContext);
294-
295-
const transaction = hub.startTransaction({
296-
name: context.functionName,
297-
op: 'function.aws.lambda',
298-
origin: 'auto.function.serverless',
299-
...traceparentData,
300-
metadata: {
301-
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
302-
source: 'component',
303-
},
304-
}) as Sentry.Transaction | undefined;
296+
let transaction: Sentry.Transaction | undefined;
297+
if (options.startTransaction) {
298+
const eventWithHeaders = event as { headers?: { [key: string]: string } };
299+
300+
const sentryTrace =
301+
eventWithHeaders.headers && isString(eventWithHeaders.headers['sentry-trace'])
302+
? eventWithHeaders.headers['sentry-trace']
303+
: undefined;
304+
const baggage = eventWithHeaders.headers?.baggage;
305+
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
306+
sentryTrace,
307+
baggage,
308+
);
309+
hub.getScope().setPropagationContext(propagationContext);
310+
311+
transaction = hub.startTransaction({
312+
name: context.functionName,
313+
op: 'function.aws.lambda',
314+
origin: 'auto.function.serverless',
315+
...traceparentData,
316+
metadata: {
317+
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
318+
source: 'component',
319+
},
320+
});
321+
}
305322

306323
const scope = hub.pushScope();
307324
let rv: TResult;
308325
try {
309326
enhanceScopeWithEnvironmentData(scope, context, START_TIME);
310-
// We put the transaction on the scope so users can attach children to it
311-
scope.setSpan(transaction);
327+
if (options.startTransaction) {
328+
enhanceScopeWithTransactionData(scope, context);
329+
// We put the transaction on the scope so users can attach children to it
330+
scope.setSpan(transaction);
331+
}
312332
rv = await asyncHandler(event, context);
313333

314334
// We manage lambdas that use Promise.allSettled by capturing the errors of failed promises

packages/serverless/test/__mocks__/@sentry/node.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const resetMocks = (): void => {
5050
fakeHub.pushScope.mockClear();
5151
fakeHub.popScope.mockClear();
5252
fakeHub.getScope.mockClear();
53+
fakeHub.startTransaction.mockClear();
5354

5455
fakeScope.addEventProcessor.mockClear();
5556
fakeScope.setTransactionName.mockClear();

packages/serverless/test/awslambda.test.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ function expectScopeSettings(fakeTransactionContext: any) {
4545
// @ts-expect-error see "Why @ts-expect-error" note
4646
const fakeTransaction = { ...SentryNode.fakeTransaction, ...fakeTransactionContext };
4747
// @ts-expect-error see "Why @ts-expect-error" note
48+
expect(SentryNode.fakeScope.setTransactionName).toBeCalledWith('functionName');
49+
// @ts-expect-error see "Why @ts-expect-error" note
4850
expect(SentryNode.fakeScope.setSpan).toBeCalledWith(fakeTransaction);
4951
// @ts-expect-error see "Why @ts-expect-error" note
5052
expect(SentryNode.fakeScope.setTag).toBeCalledWith('server_name', expect.anything());
@@ -180,11 +182,27 @@ describe('AWSLambda', () => {
180182
expect(SentryNode.captureException).toHaveBeenNthCalledWith(2, error2, expect.any(Function));
181183
expect(SentryNode.captureException).toBeCalledTimes(2);
182184
});
185+
186+
// "wrapHandler() ... successful execution" tests the default of startTransaction enabled
187+
test('startTransaction disabled', async () => {
188+
expect.assertions(3);
189+
190+
const handler: Handler = async (_event, _context) => 42;
191+
const wrappedHandler = wrapHandler(handler, { startTransaction: false });
192+
await wrappedHandler(fakeEvent, fakeContext, fakeCallback);
193+
194+
// @ts-expect-error see "Why @ts-expect-error" note
195+
expect(SentryNode.fakeScope.setTransactionName).toBeCalledTimes(0);
196+
// @ts-expect-error see "Why @ts-expect-error" note
197+
expect(SentryNode.fakeScope.setTag).toBeCalledTimes(0);
198+
// @ts-expect-error see "Why @ts-expect-error" note
199+
expect(SentryNode.fakeHub.startTransaction).toBeCalledTimes(0);
200+
});
183201
});
184202

185203
describe('wrapHandler() on sync handler', () => {
186204
test('successful execution', async () => {
187-
expect.assertions(9);
205+
expect.assertions(10);
188206

189207
const handler: Handler = (_event, _context, callback) => {
190208
callback(null, 42);
@@ -209,7 +227,7 @@ describe('AWSLambda', () => {
209227
});
210228

211229
test('unsuccessful execution', async () => {
212-
expect.assertions(9);
230+
expect.assertions(10);
213231

214232
const error = new Error('sorry');
215233
const handler: Handler = (_event, _context, callback) => {
@@ -284,7 +302,7 @@ describe('AWSLambda', () => {
284302
});
285303

286304
test('capture error', async () => {
287-
expect.assertions(9);
305+
expect.assertions(10);
288306

289307
const error = new Error('wat');
290308
const handler: Handler = (_event, _context, _callback) => {
@@ -319,7 +337,7 @@ describe('AWSLambda', () => {
319337

320338
describe('wrapHandler() on async handler', () => {
321339
test('successful execution', async () => {
322-
expect.assertions(9);
340+
expect.assertions(10);
323341

324342
const handler: Handler = async (_event, _context) => {
325343
return 42;
@@ -355,7 +373,7 @@ describe('AWSLambda', () => {
355373
});
356374

357375
test('capture error', async () => {
358-
expect.assertions(9);
376+
expect.assertions(10);
359377

360378
const error = new Error('wat');
361379
const handler: Handler = async (_event, _context) => {
@@ -401,7 +419,7 @@ describe('AWSLambda', () => {
401419

402420
describe('wrapHandler() on async handler with a callback method (aka incorrect usage)', () => {
403421
test('successful execution', async () => {
404-
expect.assertions(9);
422+
expect.assertions(10);
405423

406424
const handler: Handler = async (_event, _context, _callback) => {
407425
return 42;
@@ -437,7 +455,7 @@ describe('AWSLambda', () => {
437455
});
438456

439457
test('capture error', async () => {
440-
expect.assertions(9);
458+
expect.assertions(10);
441459

442460
const error = new Error('wat');
443461
const handler: Handler = async (_event, _context, _callback) => {

0 commit comments

Comments
 (0)