Skip to content

Commit

Permalink
fix(api-observability): log unhandled exceptions caught in exceptions…
Browse files Browse the repository at this point in the history
… filter
  • Loading branch information
timonmasberg committed Feb 1, 2024
1 parent 8683454 commit 7a748d5
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { DeepMocked, createMock } from '@golevelup/ts-jest';
import { Logger } from '@nestjs/common';
import * as Sentry from '@sentry/node';

import { PresentableException } from '@kordis/api/shared';

import { SentryExceptionsFilter } from './sentry-exceptions.filter';

describe('ExceptionsFilter', () => {
describe('SentryExceptionsFilter', () => {
let sentryExceptionsFilter: SentryExceptionsFilter;
let addBreadcrumbMock: jest.Mock;
let captureExceptionMock: jest.Mock;

beforeEach(() => {
let logger: DeepMocked<Logger>;
beforeEach(async () => {
addBreadcrumbMock = jest.fn();
captureExceptionMock = jest.fn();
logger = createMock<Logger>();

(Sentry.addBreadcrumb as jest.Mock) = addBreadcrumbMock;
(Sentry.captureException as jest.Mock) = captureExceptionMock;

sentryExceptionsFilter = new SentryExceptionsFilter();
sentryExceptionsFilter = new SentryExceptionsFilter(logger);
});

afterEach(() => {
Expand All @@ -36,15 +39,11 @@ describe('ExceptionsFilter', () => {

sentryExceptionsFilter.catch(presentableException);

expect(addBreadcrumbMock).toHaveBeenCalledTimes(1);
expect(addBreadcrumbMock).toHaveBeenCalledWith({
level: 'error',
message: 'message',
data: {
name: 'Error',
code: 'code',
stack: expect.any(String),
},
expect(logger.error).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledWith('message', undefined, {
name: 'Error',
code: 'code',
stack: expect.any(String),
});
expect(captureExceptionMock).not.toHaveBeenCalled();
});
Expand All @@ -58,6 +57,14 @@ describe('ExceptionsFilter', () => {
expect(captureExceptionMock).toHaveBeenCalledWith(exception, {
level: 'error',
});
expect(logger.error).toHaveBeenCalledTimes(1);
expect(logger.error).toHaveBeenCalledWith(
'Caught unhandled exception that was not presentable',
undefined,
{
exception,
},
);
expect(addBreadcrumbMock).not.toHaveBeenCalled();
});
});
30 changes: 21 additions & 9 deletions libs/api/observability/src/lib/filters/sentry-exceptions.filter.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,35 @@
import { Catch, ExceptionFilter } from '@nestjs/common';
import { Catch, ExceptionFilter, Logger } from '@nestjs/common';
import * as Sentry from '@sentry/node';

import { PresentableException } from '@kordis/api/shared';

import { KordisLogger } from '../services/kordis-logger.interface';

@Catch()
export class SentryExceptionsFilter implements ExceptionFilter {
readonly logger: KordisLogger;

constructor(_logger: Logger) {
this.logger = _logger;
}

catch(exception: unknown): void {
if (exception instanceof PresentableException) {
// if this is a presentable error, such as a validation error, we don't want to log it as an error but rather as an information to have the context for possible future debugging
Sentry.addBreadcrumb({
level: 'error',
message: exception.message,
data: {
code: exception.code,
name: exception.name,
stack: exception.stack,
},
this.logger.error(exception.message, undefined, {
code: exception.code,
name: exception.name,
stack: exception.stack,
});
} else {
this.logger.error(
'Caught unhandled exception that was not presentable',
undefined,
{
exception,
},
);

Sentry.captureException(exception, {
level: 'error',
});
Expand Down

0 comments on commit 7a748d5

Please sign in to comment.