Skip to content

ref(serverless): Do not throw on flush error #5090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 13, 2022
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
1 change: 1 addition & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ For our efforts to reduce bundle size of the SDK we had to remove and refactor p
- Remove `SDK_NAME` export from `@sentry/browser`, `@sentry/node`, `@sentry/tracing` and `@sentry/vue` packages.
- Removed `eventStatusFromHttpCode` to save on bundle size.
- Replace `BrowserTracing` `maxTransactionDuration` option with `finalTimeout` option
- Removed `ignoreSentryErrors` option from AWS lambda SDK. Errors originating from the SDK will now *always* be caught internally.

## Sentry Angular SDK Changes

Expand Down
10 changes: 2 additions & 8 deletions packages/serverless/src/awslambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
} from '@sentry/node';
import { extractTraceparentData } from '@sentry/tracing';
import { Integration } from '@sentry/types';
import { isString, logger, SentryError } from '@sentry/utils';
import { isString, logger } 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 Down Expand Up @@ -54,7 +54,6 @@ export interface WrapperOptions {
* @default false
*/
captureAllSettledReasons: boolean;
ignoreSentryErrors: boolean;
}

export const defaultIntegrations: Integration[] = [...Sentry.defaultIntegrations, new AWSServices({ optional: true })];
Expand Down Expand Up @@ -226,7 +225,6 @@ export function wrapHandler<TEvent, TResult>(
captureTimeoutWarning: true,
timeoutWarningLimit: 500,
captureAllSettledReasons: false,
ignoreSentryErrors: true,
...wrapOptions,
};
let timeoutWarningTimer: NodeJS.Timeout;
Expand Down Expand Up @@ -318,11 +316,7 @@ export function wrapHandler<TEvent, TResult>(
transaction.finish();
hub.popScope();
await flush(options.flushTimeout).catch(e => {
if (options.ignoreSentryErrors && e instanceof SentryError) {
IS_DEBUG_BUILD && logger.error(e);
return;
}
throw e;
IS_DEBUG_BUILD && logger.error(e);
});
}
return rv;
Expand Down
6 changes: 3 additions & 3 deletions packages/serverless/src/gcpfunction/cloud_events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ function _wrapCloudEventFunction(
transaction.finish();

void flush(options.flushTimeout)
.then(() => {
callback(...args);
})
.then(null, e => {
IS_DEBUG_BUILD && logger.error(e);
})
.then(() => {
callback(...args);
});
});

Expand Down
6 changes: 3 additions & 3 deletions packages/serverless/src/gcpfunction/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ function _wrapEventFunction(
transaction.finish();

void flush(options.flushTimeout)
.then(() => {
callback(...args);
})
.then(null, e => {
IS_DEBUG_BUILD && logger.error(e);
})
.then(() => {
callback(...args);
});
});

Expand Down
6 changes: 3 additions & 3 deletions packages/serverless/src/gcpfunction/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ function _wrapHttpFunction(fn: HttpFunction, wrapOptions: Partial<HttpFunctionWr
transaction.finish();

void flush(options.flushTimeout)
.then(() => {
_end.call(this, chunk, encoding, cb);
})
.then(null, e => {
IS_DEBUG_BUILD && logger.error(e);
})
.then(() => {
_end.call(this, chunk, encoding, cb);
});
};

Expand Down
54 changes: 15 additions & 39 deletions packages/serverless/test/awslambda.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { SentryError } 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 { Callback, Handler } from 'aws-lambda';
Expand Down Expand Up @@ -178,44 +177,6 @@ describe('AWSLambda', () => {
expect(Sentry.captureException).toHaveBeenNthCalledWith(2, error2);
expect(Sentry.captureException).toBeCalledTimes(2);
});

test('ignoreSentryErrors - with successful handler', async () => {
const sentryError = new SentryError('HTTP Error (429)');
jest.spyOn(Sentry, 'flush').mockRejectedValueOnce(sentryError);

const handledError = new Error('handled error, and we want to monitor it');
const expectedSuccessStatus = { status: 'success', reason: 'we handled error, so success' };
const handler = () => {
Sentry.captureException(handledError);
return Promise.resolve(expectedSuccessStatus);
};
const wrappedHandlerWithoutIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: false });
const wrappedHandlerWithIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: true });

await expect(wrappedHandlerWithoutIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).rejects.toThrow(
sentryError,
);
await expect(wrappedHandlerWithIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).resolves.toBe(
expectedSuccessStatus,
);
});

test('ignoreSentryErrors - with failed handler', async () => {
const sentryError = new SentryError('HTTP Error (429)');
jest.spyOn(Sentry, 'flush').mockRejectedValueOnce(sentryError);

const criticalUnhandledError = new Error('critical unhandled error ');
const handler = () => Promise.reject(criticalUnhandledError);
const wrappedHandlerWithoutIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: false });
const wrappedHandlerWithIgnoringSentryErrors = wrapHandler(handler, { ignoreSentryErrors: true });

await expect(wrappedHandlerWithoutIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).rejects.toThrow(
sentryError,
);
await expect(wrappedHandlerWithIgnoringSentryErrors(fakeEvent, fakeContext, fakeCallback)).rejects.toThrow(
criticalUnhandledError,
);
});
});

describe('wrapHandler() on sync handler', () => {
Expand Down Expand Up @@ -345,6 +306,21 @@ describe('AWSLambda', () => {
expect(Sentry.flush).toBeCalled();
}
});

test('should not throw when flush rejects', async () => {
const handler: Handler = async () => {
// Friendly handler with no errors :)
return 'some string';
};

const wrappedHandler = wrapHandler(handler);

jest.spyOn(Sentry, 'flush').mockImplementationOnce(async () => {
throw new Error();
});

await expect(wrappedHandler(fakeEvent, fakeContext, fakeCallback)).resolves.toBe('some string');
});
});

describe('wrapHandler() on async handler with a callback method (aka incorrect usage)', () => {
Expand Down
28 changes: 28 additions & 0 deletions packages/serverless/test/gcpfunction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,34 @@ describe('GCPFunction', () => {
expect(Sentry.fakeTransaction.finish).toBeCalled();
expect(Sentry.flush).toBeCalled();
});

test('should not throw when flush rejects', async () => {
expect.assertions(2);

const handler: HttpFunction = async (_req, res) => {
res.statusCode = 200;
res.end();
};

const wrappedHandler = wrapHttpFunction(handler);

const request = {
method: 'POST',
url: '/path?q=query',
headers: { host: 'hostname', 'content-type': 'application/json' },
body: { foo: 'bar' },
} as Request;

const mockEnd = jest.fn();
const response = { end: mockEnd } as unknown as Response;

jest.spyOn(Sentry, 'flush').mockImplementationOnce(async () => {
throw new Error();
});

await expect(wrappedHandler(request, response)).resolves.toBeUndefined();
expect(mockEnd).toHaveBeenCalledTimes(1);
});
});

test('wrapHttpFunction request data', async () => {
Expand Down