diff --git a/src/interfaces.ts b/src/interfaces.ts index 265fe4c..7b721d4 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -22,5 +22,6 @@ export interface CosmasOptions { config?: LoggerOptions; pretty?: boolean; sentry?: string | boolean; + sentryLevel?: pino.LevelWithSilent; skip?: (req: Request, res?: Response) => boolean; } diff --git a/src/sentry.ts b/src/sentry.ts index 03454e9..bab309d 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -1,5 +1,7 @@ import { captureException, captureMessage, Severity, withScope } from '@sentry/node'; import { Transform, TransformCallback } from 'stream'; +import { CosmasOptions } from './interfaces'; +import { levels } from './levels'; const reportToSentry = (obj: any) => { if (!obj.stack) { @@ -21,17 +23,20 @@ const PINO_TO_SENTRY: { [key: number]: Severity } = { 60: Severity.Critical, }; -class SentryTransformStream extends Transform { - // tslint:disable-next-line:function-name - public _transform(chunk: any, _encoding: string, callback: TransformCallback) { - const obj = JSON.parse(chunk); - withScope(scope => { - scope.setLevel(PINO_TO_SENTRY[obj.level]); - scope.setExtras(obj); - reportToSentry(obj); - }); - this.push(chunk); - callback(); - } -} -export { SentryTransformStream }; +export const createSentryTransformStream = (options: CosmasOptions): any => { + return class SentryTransformStream extends Transform { + // tslint:disable-next-line:function-name + public _transform(chunk: any, _encoding: string, callback: TransformCallback) { + const obj = JSON.parse(chunk); + if (obj.level >= (options.sentryLevel || levels.warn)) { + withScope(scope => { + scope.setLevel(PINO_TO_SENTRY[obj.level]); + scope.setExtras(obj); + reportToSentry(obj); + }); + } + this.push(chunk); + callback(); + } + }; +}; diff --git a/src/streams.ts b/src/streams.ts index a413cd0..6951747 100644 --- a/src/streams.ts +++ b/src/streams.ts @@ -73,8 +73,8 @@ const initLoggerStreams = ( streams = decorateStreams(streams, getDefaultTransformStream(options)); if (options.sentry) { - const { SentryTransformStream } = require('./sentry'); - streams = decorateStreams(streams, SentryTransformStream); + const { createSentryTransformStream } = require('./sentry'); + streams = decorateStreams(streams, createSentryTransformStream(options)); } return streams; diff --git a/src/tests/sentry-mocked.test.ts b/src/tests/sentry-mocked.test.ts index c96a01a..a40e5a3 100644 --- a/src/tests/sentry-mocked.test.ts +++ b/src/tests/sentry-mocked.test.ts @@ -1,3 +1,5 @@ +import { levels } from '../levels'; + let loggerFactory; const scope: any = {}; const withScope = jest.fn(fn => @@ -60,31 +62,38 @@ describe('sentry mocked', () => { `); }); - test('sentry captureMessage is called with correct scope', async () => { + test('sentry captureMessage is called with correct scope (respects sentry level)', async () => { await new Promise((resolve, reject) => { const logger = loggerFactory({ sentry: 'DSN', + sentryLevel: levels.fatal, }); captureMessage.mockImplementation(createCapture(resolve)); - logger.info('Foo'); + // expect to trigger only fatal + logger.trace('trace'); + logger.debug('debug'); + logger.info('info'); + logger.warn('warn'); + logger.error('error'); + logger.fatal('fatal'); }); expect(captureMessage).toHaveBeenCalledTimes(1); expect(captureException).not.toHaveBeenCalled(); expect(captureMessage.mock.calls[0]).toMatchInlineSnapshot(` Array [ - "Foo", + "fatal", ] `); expect(captureMessage.mock.results[0].value).toMatchInlineSnapshot(` Object { - "data": "Foo", + "data": "fatal", "scope": Object { "extras": Object { - "level": 30, - "message": "Foo", + "level": 60, + "message": "fatal", "v": 1, }, - "level": "info", + "level": "critical", }, } `);