Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all \u003cpre\u003e\u003ccode\u003e blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks"); } } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); } })(); (function(){ try { var __m = "github.com"; var __re = new RegExp('^' + "github\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length \u003e 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 10 additions & 54 deletions packages/serverless/src/awslambda.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,14 +4,12 @@ import {
flush,
getCurrentHub,
Scope,
SDK_VERSION,
Severity,
startTransaction,
withScope,
} from '@sentry/node';
import * as Sentry from '@sentry/node';
import { Integration } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
// 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
// eslint-disable-next-line import/no-unresolved
import { Context, Handler } from 'aws-lambda';
Expand All@@ -20,6 +18,7 @@ import { performance } from 'perf_hooks';
import { types } from 'util';

import { AWSServices } from './awsservices';
import { serverlessEventProcessor } from './utils';

export * from '@sentry/node';

Expand DownExpand Up@@ -54,37 +53,8 @@ export function init(options: Sentry.NodeOptions = {}): void {
if (options.defaultIntegrations === undefined) {
options.defaultIntegrations = defaultIntegrations;
}
return Sentry.init(options);
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'AWSLambda'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
});
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('AWSLambda'));
}

/**
Expand DownExpand Up@@ -125,20 +95,6 @@ function enhanceScopeWithEnvironmentData(scope: Scope, context: Context): void {
});
}

/**
* Capture exception with a a context.
*
* @param e exception to be captured
* @param context Context
*/
function captureExceptionWithContext(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
captureException(e);
});
}

/**
* Wraps a lambda handler adding it error capture and tracing capabilities.
*
Expand DownExpand Up@@ -205,8 +161,6 @@ export function wrapHandler<TEvent, TResult>(

timeoutWarningTimer = setTimeout(() => {
withScope(scope => {
addServerlessEventProcessor(scope);
enhanceScopeWithEnvironmentData(scope, context);
scope.setTag('timeout', humanReadableTimeout);
captureMessage(`Possible function timeout: ${context.functionName}`, Severity.Warning);
});
Expand All@@ -217,22 +171,24 @@ export function wrapHandler<TEvent, TResult>(
name: context.functionName,
op: 'awslambda.handler',
});
// We put the transaction on the scope so users can attach children to it
getCurrentHub().configureScope(scope => {
scope.setSpan(transaction);
});

const hub = getCurrentHub();
const scope = hub.pushScope();
let rv: TResult | undefined;
try {
enhanceScopeWithEnvironmentData(scope, context);
// We put the transaction on the scope so users can attach children to it
scope.setSpan(transaction);
rv = await asyncHandler(event, context);
} catch (e) {
captureExceptionWithContext(e, context);
captureException(e);
if (options.rethrowAfterCapture) {
throw e;
}
} finally {
clearTimeout(timeoutWarningTimer);
transaction.finish();
hub.popScope();
await flush(options.flushTimeout);
}
return rv;
Expand Down
16 changes: 9 additions & 7 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,10 @@ import {
CloudEventFunction,
CloudEventFunctionWithCallback,
} from '@google-cloud/functions-framework/build/src/functions';
import { flush, getCurrentHub, startTransaction } from '@sentry/node';
import { captureException, flush, getCurrentHub, startTransaction } from '@sentry/node';
import { logger } from '@sentry/utils';

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type CloudEventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

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

import { captureEventError, getActiveDomain, WrapperOptions } from './general';
import { configureScopeWithContext, getActiveDomain, WrapperOptions } from './general';

export type EventFunctionWrapperOptions = WrapperOptions;

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

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

const activeDomain = getActiveDomain();

activeDomain.on('error', err => {
captureEventError(err, context);
});
activeDomain.on('error', captureException);

const newCallback = activeDomain.bind((...args: unknown[]) => {
if (args[0] !== null && args[0] !== undefined) {
captureEventError(args[0], context);
captureException(args[0]);
}
transaction.finish();

Expand Down
53 changes: 9 additions & 44 deletions packages/serverless/src/gcpfunction/general.ts
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { Context } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, Scope, SDK_VERSION, withScope } from '@sentry/node';
import { Scope } from '@sentry/node';
import { Context as SentryContext } from '@sentry/types';
import { addExceptionMechanism } from '@sentry/utils';
import * as domain from 'domain';
import { hostname } from 'os';

Expand All@@ -12,52 +11,18 @@ export interface WrapperOptions {
}

/**
* Capture exception with additional event information.
* Enhances the scope with additional event information.
*
* @param e exception to be captured
* @param scope scope
* @param context event context
*/
export function captureEventError(e: unknown, context: Context): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
captureException(e);
});
}

/**
* Add event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param scope Scope that processor should be added to
*/
export function addServerlessEventProcessor(scope: Scope): void {
scope.addEventProcessor(event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), 'GCPFunction'],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
export function configureScopeWithContext(scope: Scope, context: Context): void {
scope.setContext('runtime', {
name: 'node',
version: global.process.version,
});
scope.setTag('server_name', process.env.SENTRY_NAME || hostname());
scope.setContext('gcp.function.context', { ...context } as SentryContext);
}

/**
Expand Down
27 changes: 8 additions & 19 deletions packages/serverless/src/gcpfunction/http.ts
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
// '@google-cloud/functions-framework/build/src/functions' import is expected to be type-only so it's erased in the final .js file.
// When TypeScript compiler is upgraded, use `import type` syntax to explicitly assert that we don't want to load a module here.
import { HttpFunction } from '@google-cloud/functions-framework/build/src/functions';
import { captureException, flush, getCurrentHub, Handlers, startTransaction, withScope } from '@sentry/node';
import { captureException, flush, getCurrentHub, Handlers, startTransaction } from '@sentry/node';
import { logger, stripUrlQueryAndFragment } from '@sentry/utils';

import { addServerlessEventProcessor, getActiveDomain, WrapperOptions } from './general';
import { getActiveDomain, WrapperOptions } from './general';

type Request = Parameters<HttpFunction>[0];
type Response = Parameters<HttpFunction>[1];
Expand All@@ -18,21 +18,6 @@ export { Request, Response };

const { parseRequest } = Handlers;

/**
* Capture exception with additional request information.
*
* @param e exception to be captured
* @param req incoming request
* @param options request capture options
*/
function captureRequestError(e: unknown, req: Request, options: ParseRequestOptions): void {
withScope(scope => {
addServerlessEventProcessor(scope);
scope.addEventProcessor(event => parseRequest(event, req, options));
captureException(e);
});
}

/**
* Wraps an HTTP function handler adding it error capture and tracing capabilities.
*
Expand All@@ -58,8 +43,12 @@ export function wrapHttpFunction(
op: 'gcp.function.http',
});

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

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

// eslint-disable-next-line @typescript-eslint/unbound-method
Expand Down
13 changes: 12 additions & 1 deletion packages/serverless/src/gcpfunction/index.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
import * as Sentry from '@sentry/node';

import { serverlessEventProcessor } from '../utils';

export * from './http';
export * from './events';
export * from './cloud_events';
export { init } from '@sentry/node';

/**
* @see {@link Sentry.init}
*/
export function init(options: Sentry.NodeOptions = {}): void {
Sentry.init(options);
Sentry.addGlobalEventProcessor(serverlessEventProcessor('GCPFunction'));
}
33 changes: 33 additions & 0 deletions packages/serverless/src/utils.ts
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
import { Event, SDK_VERSION } from '@sentry/node';
import { addExceptionMechanism } from '@sentry/utils';

/**
* Event processor that will override SDK details to point to the serverless SDK instead of Node,
* as well as set correct mechanism type, which should be set to `handled: false`.
* We do it like this, so that we don't introduce any side-effects in this module, which makes it tree-shakeable.
* @param event Event
* @param integration Name of the serverless integration ('AWSLambda', 'GCPFunction', etc)
*/
export function serverlessEventProcessor(integration: string): (event: Event) => Event {
return event => {
event.sdk = {
...event.sdk,
name: 'sentry.javascript.serverless',
integrations: [...((event.sdk && event.sdk.integrations) || []), integration],
packages: [
...((event.sdk && event.sdk.packages) || []),
{
name: 'npm:@sentry/serverless',
version: SDK_VERSION,
},
],
version: SDK_VERSION,
};

addExceptionMechanism(event, {
handled: false,
});

return event;
};
}
Loading