Skip to content

Commit

Permalink
fix(common): CHECKOUT-4418 Log error in console
Browse files Browse the repository at this point in the history
  • Loading branch information
davidchin committed Sep 18, 2019
1 parent f08dcdc commit 5a35293
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 16 deletions.
77 changes: 77 additions & 0 deletions src/app/common/error/ConsoleErrorLogger.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import ConsoleErrorLogger from './ConsoleErrorLogger';
import { ErrorLevelType } from './ErrorLogger';

describe('ConsoleErrorLogger', () => {
let mockConsole: Console;

beforeEach(() => {
mockConsole = {
error: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
} as unknown as Console;
});

it('logs error as error to console by default', () => {
const logger = new ConsoleErrorLogger({ console: mockConsole });
const error = new Error('Testing 123');
const tags = { errorCode: 'abc' };

logger.log(error, tags);

expect(mockConsole.error)
.toHaveBeenCalledWith(error, tags);
});

it('logs error as warning to console', () => {
const logger = new ConsoleErrorLogger({ console: mockConsole });
const error = new Error('Testing 123');
const tags = { errorCode: 'abc' };

logger.log(error, tags, ErrorLevelType.Warning);

expect(mockConsole.warn)
.toHaveBeenCalledWith(error, tags);
});

it('logs error as info to console', () => {
const logger = new ConsoleErrorLogger({ console: mockConsole });
const error = new Error('Testing 123');
const tags = { errorCode: 'abc' };

logger.log(error, tags, ErrorLevelType.Info);

expect(mockConsole.info)
.toHaveBeenCalledWith(error, tags);
});

it('allows additional error types to be logged', () => {
const logger = new ConsoleErrorLogger({
console: mockConsole,
errorTypes: ['Foo'],
});

const error = new Error('Foo');
error.name = 'Foo';

logger.log(error);

expect(mockConsole.error)
.toHaveBeenCalledWith(error, undefined);
});

it('does not log custom errors unless they are listed as additional error types', () => {
const logger = new ConsoleErrorLogger({
console: mockConsole,
errorTypes: ['Foo'],
});

const error = new Error('Bar');
error.name = 'Bar';

logger.log(error);

expect(mockConsole.error)
.not.toHaveBeenCalledWith(error, undefined);
});
});
51 changes: 51 additions & 0 deletions src/app/common/error/ConsoleErrorLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { includes } from 'lodash';

import DEFAULT_ERROR_TYPES from './defaultErrorTypes';
import ErrorLogger, { ErrorLevelType, ErrorTags } from './ErrorLogger';

export interface ConsoleErrorLoggerOptions {
console?: Console;
errorTypes?: string[];
}

// tslint:disable:no-console
export default class ConsoleErrorLogger implements ErrorLogger {
private console: Console;
private errorTypes: string[];

constructor(
options?: ConsoleErrorLoggerOptions
) {
const {
console: customConsole = console,
errorTypes = [],
} = options || {};

this.console = customConsole;
this.errorTypes = [
...DEFAULT_ERROR_TYPES,
...errorTypes,
];
}

log(
error: Error,
tags?: ErrorTags,
level: ErrorLevelType = ErrorLevelType.Error
): void {
if (!includes(this.errorTypes, error.name)) {
return;
}

switch (level) {
case ErrorLevelType.Error:
return this.console.error(error, tags);

case ErrorLevelType.Info:
return this.console.info(error, tags);

case ErrorLevelType.Warning:
return this.console.warn(error, tags);
}
}
}
21 changes: 20 additions & 1 deletion src/app/common/error/SentryErrorLogger.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { RewriteFrames } from '@sentry/integrations';
import { Integration } from '@sentry/types';

import computeErrorCode from './computeErrorCode';
import DEFAULT_ERROR_TYPES from './defaultErrorTypes';
import ConsoleErrorLogger from './ConsoleErrorLogger';
import { ErrorLevelType } from './ErrorLogger';
import SentryErrorLogger, { DEFAULT_ERROR_TYPES } from './SentryErrorLogger';
import SentryErrorLogger from './SentryErrorLogger';

jest.mock('@sentry/browser', () => {
return {
Expand Down Expand Up @@ -254,5 +256,22 @@ describe('SentryErrorLogger', () => {
expect(scope.setLevel)
.toHaveBeenNthCalledWith(3, Severity.Info);
});

it('logs error in console if console logger is provided', () => {
const consoleLogger = new ConsoleErrorLogger();

jest.spyOn(consoleLogger, 'log')
.mockImplementation();

const logger = new SentryErrorLogger(config, { consoleLogger });
const error = new Error('Testing 123');
const tags = { errorCode: 'abc' };
const level = ErrorLevelType.Error;

logger.log(error, tags, level);

expect(consoleLogger.log)
.toHaveBeenCalledWith(error, tags, level);
});
});
});
33 changes: 20 additions & 13 deletions src/app/common/error/SentryErrorLogger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,38 @@ import { EventHint } from '@sentry/types';
import { includes, isEmpty } from 'lodash';

import computeErrorCode from './computeErrorCode';
import ErrorLogger, { ErrorLevelType, ErrorLoggerOptions, ErrorTags } from './ErrorLogger';

export const DEFAULT_ERROR_TYPES = [
'Error',
'EvalError',
'RangeError',
'ReferenceError',
'SyntaxError',
'TypeError',
'URIError',
];
import DEFAULT_ERROR_TYPES from './defaultErrorTypes';
import ConsoleErrorLogger from './ConsoleErrorLogger';
import ErrorLogger, { ErrorLevelType, ErrorTags } from './ErrorLogger';
import NoopErrorLogger from './NoopErrorLogger';

export interface SentryErrorLoggerOptions {
consoleLogger?: ConsoleErrorLogger;
errorTypes?: string[];
publicPath?: string;
}

export default class SentryErrorLogger implements ErrorLogger {
private consoleLogger: ErrorLogger;
private errorTypes: string[];
private publicPath: string;

constructor(
config: BrowserOptions,
options?: Omit<ErrorLoggerOptions, 'serviceConfig'>
options?: SentryErrorLoggerOptions
) {
const { errorTypes = [], publicPath = '' } = options || {};
const {
consoleLogger = new NoopErrorLogger(),
errorTypes = [],
publicPath = '',
} = options || {};

this.errorTypes = [
...DEFAULT_ERROR_TYPES,
...errorTypes,
];

this.consoleLogger = consoleLogger;
this.publicPath = publicPath;

init({
Expand All @@ -49,6 +54,8 @@ export default class SentryErrorLogger implements ErrorLogger {
tags?: ErrorTags,
level: ErrorLevelType = ErrorLevelType.Error
): void {
this.consoleLogger.log(error, tags, level);

withScope(scope => {
const { errorCode = computeErrorCode(error) } = tags || {};
const fingerprint = ['{{ default }}', errorCode];
Expand Down
9 changes: 7 additions & 2 deletions src/app/common/error/createErrorLogger.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import ConsoleErrorLogger from './ConsoleErrorLogger';
import ErrorLogger, { ErrorLoggerOptions, ErrorLoggerServiceConfig } from './ErrorLogger';
import NoopErrorLogger from './NoopErrorLogger';
import SentryErrorLogger from './SentryErrorLogger';
Expand All @@ -9,9 +10,13 @@ export default function createErrorLogger(
if (serviceConfig && serviceConfig.sentry) {
return new SentryErrorLogger(
serviceConfig.sentry,
options
{ ...options, consoleLogger: new ConsoleErrorLogger(options) }
);
}

return new NoopErrorLogger();
if (process.env.NODE_ENV === 'test') {
return new NoopErrorLogger();
}

return new ConsoleErrorLogger(options);
}
11 changes: 11 additions & 0 deletions src/app/common/error/defaultErrorTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const DEFAULT_ERROR_TYPES = [
'Error',
'EvalError',
'RangeError',
'ReferenceError',
'SyntaxError',
'TypeError',
'URIError',
];

export default DEFAULT_ERROR_TYPES;

0 comments on commit 5a35293

Please sign in to comment.