Skip to content

Commit a36385b

Browse files
committed
Pass GCPFunction scope settings for transactions
1 parent 750e493 commit a36385b

File tree

7 files changed

+148
-118
lines changed

7 files changed

+148
-118
lines changed

packages/serverless/src/awslambda.ts

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@ import {
44
flush,
55
getCurrentHub,
66
Scope,
7-
SDK_VERSION,
87
Severity,
98
startTransaction,
109
withScope,
1110
} from '@sentry/node';
1211
import * as Sentry from '@sentry/node';
1312
import { Integration } from '@sentry/types';
14-
import { addExceptionMechanism } from '@sentry/utils';
1513
// NOTE: I have no idea how to fix this right now, and don't want to waste more time, as it builds just fine — Kamil
1614
// eslint-disable-next-line import/no-unresolved
1715
import { Context, Handler } from 'aws-lambda';
@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
2018
import { types } from 'util';
2119

2220
import { AWSServices } from './awsservices';
21+
import { addServerlessEventProcessor as addServerlessEventProcessorGeneric } from './utils';
2322

2423
export * from '@sentry/node';
2524

@@ -64,27 +63,7 @@ export function init(options: Sentry.NodeOptions = {}): void {
6463
* @param scope Scope that processor should be added to
6564
*/
6665
function addServerlessEventProcessor(scope: Scope): void {
67-
scope.addEventProcessor(event => {
68-
event.sdk = {
69-
...event.sdk,
70-
name: 'sentry.javascript.serverless',
71-
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
72-
packages: [
73-
...((event.sdk && event.sdk.packages) || []),
74-
{
75-
name: 'npm:@sentry/serverless',
76-
version: SDK_VERSION,
77-
},
78-
],
79-
version: SDK_VERSION,
80-
};
81-
82-
addExceptionMechanism(event, {
83-
handled: false,
84-
});
85-
86-
return event;
87-
});
66+
addServerlessEventProcessorGeneric(scope, 'AWSLambda');
8867
}
8968

9069
/**

packages/serverless/src/gcpfunction/cloud_events.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {
44
CloudEventFunction,
55
CloudEventFunctionWithCallback,
66
} from '@google-cloud/functions-framework/build/src/functions';
7-
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
7+
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
88
import { logger } from '@sentry/utils';
99

10-
import { captureEventError, getActiveDomain, WrapperOptions } from './general';
10+
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';
1111

1212
export type CloudEventFunctionWrapperOptions = WrapperOptions;
1313

@@ -32,20 +32,22 @@ export function wrapCloudEventFunction(
3232
op: 'gcp.function.cloud_event',
3333
});
3434

35-
// We put the transaction on the scope so users can attach children to it
35+
// getCurrentHub() is expected to use current active domain as a carrier
36+
// since functions-framework creates a domain for each incoming request.
37+
// So adding of event processors every time should not lead to memory bloat.
3638
getCurrentHub().configureScope(scope => {
39+
configureScopeWithContext(scope, context);
40+
// We put the transaction on the scope so users can attach children to it
3741
scope.setSpan(transaction);
3842
});
3943

4044
const activeDomain = getActiveDomain();
4145

42-
activeDomain.on('error', err => {
43-
captureEventError(err, context);
44-
});
46+
activeDomain.on('error', captureException);
4547

4648
const newCallback = activeDomain.bind((...args: unknown[]) => {
4749
if (args[0] !== null && args[0] !== undefined) {
48-
captureEventError(args[0], context);
50+
captureException(args[0]);
4951
}
5052
transaction.finish();
5153

packages/serverless/src/gcpfunction/events.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
22
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
33
import { EventFunction, EventFunctionWithCallback } from '@google-cloud/functions-framework/build/src/functions';
4-
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
4+
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
55
import { logger } from '@sentry/utils';
66

7-
import { captureEventError, getActiveDomain, WrapperOptions } from './general';
7+
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';
88

99
export type EventFunctionWrapperOptions = WrapperOptions;
1010

@@ -29,20 +29,22 @@ export function wrapEventFunction(
2929
op: 'gcp.function.event',
3030
});
3131

32-
// We put the transaction on the scope so users can attach children to it
32+
// getCurrentHub() is expected to use current active domain as a carrier
33+
// since functions-framework creates a domain for each incoming request.
34+
// So adding of event processors every time should not lead to memory bloat.
3335
getCurrentHub().configureScope(scope => {
36+
configureScopeWithContext(scope, context);
37+
// We put the transaction on the scope so users can attach children to it
3438
scope.setSpan(transaction);
3539
});
3640

3741
const activeDomain = getActiveDomain();
3842

39-
activeDomain.on('error', err => {
40-
captureEventError(err, context);
41-
});
43+
activeDomain.on('error', captureException);
4244

4345
const newCallback = activeDomain.bind((...args: unknown[]) => {
4446
if (args[0] !== null && args[0] !== undefined) {
45-
captureEventError(args[0], context);
47+
captureException(args[0]);
4648
}
4749
transaction.finish();
4850

packages/serverless/src/gcpfunction/general.ts

Lines changed: 13 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,31 @@
11
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
22
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
33
import { Context } from '@google-cloud/functions-framework/build/src/functions';
4-
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
4+
import { Scope } from '@sentry/node';
55
import { Context as SentryContext } from '@sentry/types';
6-
import { addExceptionMechanism } from '@sentry/utils';
76
import * as domain from 'domain';
87
import { hostname } from 'os';
98

9+
import { addServerlessEventProcessor as addServerlessEventProcessorGeneric } from '../utils';
10+
1011
export interface WrapperOptions {
1112
flushTimeout: number;
1213
}
1314

1415
/**
15-
* Capture exception with additional event information.
16+
* Enhances the scope with additional event information.
1617
*
17-
* @param e exception to be captured
18+
* @param scope scope
1819
* @param context event context
1920
*/
20-
export function captureEventError(e: unknown, context: Context): void {
21-
withScope(scope => {
22-
addServerlessEventProcessor(scope);
23-
scope.setContext('runtime', {
24-
name: 'node',
25-
version: global.process.version,
26-
});
27-
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
28-
scope.setContext('gcp.function.context', { ...context } as SentryContext);
29-
captureException(e);
21+
export function configureScopeWithContext(scope: Scope, context: Context): void {
22+
addServerlessEventProcessor(scope);
23+
scope.setContext('runtime', {
24+
name: 'node',
25+
version: global.process.version,
3026
});
27+
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
28+
scope.setContext('gcp.function.context', { ...context } as SentryContext);
3129
}
3230

3331
/**
@@ -37,27 +35,7 @@ export function captureEventError(e: unknown, context: Context): void {
3735
* @param scope Scope that processor should be added to
3836
*/
3937
export function addServerlessEventProcessor(scope: Scope): void {
40-
scope.addEventProcessor(event => {
41-
event.sdk = {
42-
...event.sdk,
43-
name: 'sentry.javascript.serverless',
44-
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
45-
packages: [
46-
...((event.sdk && event.sdk.packages) || []),
47-
{
48-
name: 'npm:@sentry/serverless',
49-
version: SDK_VERSION,
50-
},
51-
],
52-
version: SDK_VERSION,
53-
};
54-
55-
addExceptionMechanism(event, {
56-
handled: false,
57-
});
58-
59-
return event;
60-
});
38+
addServerlessEventProcessorGeneric(scope, 'GCPFunction');
6139
}
6240

6341
/**

packages/serverless/src/gcpfunction/http.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
22
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
33
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
4-
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
4+
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
55
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';
66

77
import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
@@ -18,21 +18,6 @@ export { Request, Response };
1818

1919
const { parseRequest } = Handlers;
2020

21-
/**
22-
* Capture exception with additional request information.
23-
*
24-
* @param e exception to be captured
25-
* @param req incoming request
26-
* @param options request capture options
27-
*/
28-
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
29-
withScope(scope => {
30-
addServerlessEventProcessor(scope);
31-
scope.addEventProcessor(event => parseRequest(event, req, options));
32-
captureException(e);
33-
});
34-
}
35-
3621
/**
3722
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
3823
*
@@ -58,8 +43,13 @@ export function wrapHttpFunction(
5843
op: 'gcp.function.http',
5944
});
6045

61-
// We put the transaction on the scope so users can attach children to it
46+
// getCurrentHub() is expected to use current active domain as a carrier
47+
// since functions-framework creates a domain for each incoming request.
48+
// So adding of event processors every time should not lead to memory bloat.
6249
getCurrentHub().configureScope(scope => {
50+
addServerlessEventProcessor(scope);
51+
scope.addEventProcessor(event => parseRequest(event, req, options.parseRequestOptions));
52+
// We put the transaction on the scope so users can attach children to it
6353
scope.setSpan(transaction);
6454
});
6555

@@ -71,7 +61,7 @@ export function wrapHttpFunction(
7161
// functions-framework creates a domain for each incoming request so we take advantage of this fact and add an error handler.
7262
// BTW this is the only way to catch any exception occured during request lifecycle.
7363
getActiveDomain().on('error', err => {
74-
captureRequestError(err, req, options.parseRequestOptions);
64+
captureException(err);
7565
});
7666

7767
// eslint-disable-next-line @typescript-eslint/unbound-method

packages/serverless/src/utils.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Scope, SDK_VERSION } from '@sentry/node';
2+
import { addExceptionMechanism } from '@sentry/utils';
3+
4+
/**
5+
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
6+
* as well as set correct mechanism type, which should be set to `handled: false`.
7+
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
8+
* @param scope Scope that processor should be added to
9+
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
10+
*/
11+
export function addServerlessEventProcessor(scope: Scope, integration: string): void {
12+
scope.addEventProcessor(event => {
13+
event.sdk = {
14+
...event.sdk,
15+
name: 'sentry.javascript.serverless',
16+
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
17+
packages: [
18+
...((event.sdk && event.sdk.packages) || []),
19+
{
20+
name: 'npm:@sentry/serverless',
21+
version: SDK_VERSION,
22+
},
23+
],
24+
version: SDK_VERSION,
25+
};
26+
27+
addExceptionMechanism(event, {
28+
handled: false,
29+
});
30+
31+
return event;
32+
});
33+
}

0 commit comments

Comments
 (0)