Skip to content

Commit 093da76

Browse files
committed
feat(serverlesss): allow disabling transaction traces
1 parent 7062449 commit 093da76

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
@@ -45,6 +45,12 @@ export interface WrapperOptions {
4545
* @default false
4646
*/
4747
captureAllSettledReasons: boolean;
48+
/**
49+
* Automatically trace all handler invocations.
50+
* You may want to disable this if you use express within Lambda (use tracingHandler instead).
51+
* @default true
52+
*/
53+
startTransaction: boolean;
4854
}
4955

5056
export const defaultIntegrations: Integration[] = [...Sentry.defaultIntegrations, new AWSServices({ optional: true })];
@@ -175,11 +181,6 @@ function tryGetRemainingTimeInMillis(context: Context): number {
175181
* @param startTime performance.now() when wrapHandler was invoked
176182
*/
177183
function enhanceScopeWithEnvironmentData(scope: Scope, context: Context, startTime: number): void {
178-
scope.setTransactionName(context.functionName);
179-
180-
scope.setTag('server_name', process.env._AWS_XRAY_DAEMON_ADDRESS || process.env.SENTRY_NAME || hostname());
181-
scope.setTag('url', `awslambda:///${context.functionName}`);
182-
183184
scope.setContext('aws.lambda', {
184185
aws_request_id: context.awsRequestId,
185186
function_name: context.functionName,
@@ -201,6 +202,18 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context, startTi
201202
});
202203
}
203204

205+
/**
206+
* Adds additional transaction-related information from the environment and AWS Context to the Sentry Scope.
207+
*
208+
* @param scope Scope that should be enhanced
209+
* @param context AWS Lambda context that will be used to extract some part of the data
210+
*/
211+
function enhanceScopeWithTransactionData(scope: Scope, context: Context): void {
212+
scope.setTransactionName(context.functionName);
213+
scope.setTag('server_name', process.env._AWS_XRAY_DAEMON_ADDRESS || process.env.SENTRY_NAME || hostname());
214+
scope.setTag('url', `awslambda:///${context.functionName}`);
215+
}
216+
204217
/**
205218
* Wraps a lambda handler adding it error capture and tracing capabilities.
206219
*
@@ -219,6 +232,7 @@ export function wrapHandler<TEvent, TResult>(
219232
captureTimeoutWarning: true,
220233
timeoutWarningLimit: 500,
221234
captureAllSettledReasons: false,
235+
startTransaction: true,
222236
...wrapOptions,
223237
};
224238
let timeoutWarningTimer: NodeJS.Timeout;
@@ -276,36 +290,42 @@ export function wrapHandler<TEvent, TResult>(
276290

277291
const hub = getCurrentHub();
278292

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

303320
const scope = hub.pushScope();
304321
let rv: TResult;
305322
try {
306323
enhanceScopeWithEnvironmentData(scope, context, START_TIME);
307-
// We put the transaction on the scope so users can attach children to it
308-
scope.setSpan(transaction);
324+
if (options.startTransaction) {
325+
enhanceScopeWithTransactionData(scope, context);
326+
// We put the transaction on the scope so users can attach children to it
327+
scope.setSpan(transaction);
328+
}
309329
rv = await asyncHandler(event, context);
310330

311331
// 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)