Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 76872b2

Browse files
author
Michal Vlasák
committed
♻️ Log to Sentry without transform stream
1 parent d39799d commit 76872b2

5 files changed

Lines changed: 31 additions & 34 deletions

File tree

src/index.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,6 @@ const defaultLogger = (options: CosmasOptions & { loggerName?: string } = {}): C
123123
serializers.disablePaths(options.disableFields);
124124
serializers.enablePaths(options.enableFields);
125125

126-
if (options.sentry) {
127-
const sentry = require('@sentry/node');
128-
if (typeof options.sentry === 'string') {
129-
sentry.init({ dsn: options.sentry });
130-
}
131-
}
132-
133126
const isTesting = process.env.NODE_ENV === 'test';
134127
const defaultLevel: Level = options.defaultLevel || (isTesting ? 'silent' : 'debug');
135128
const messageKey = 'message'; // best option for Google Stackdriver,

src/sentry.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { captureException, captureMessage, Severity, withScope } from '@sentry/node';
2-
import { Transform, TransformCallback } from 'stream';
3-
import { CosmasOptions } from './interfaces';
2+
import { streamSym } from 'pino/lib/symbols';
3+
import { Cosmas } from '.';
44
import { levels } from './levels';
55

66
const reportToSentry = (obj: any) => {
@@ -23,20 +23,24 @@ const PINO_TO_SENTRY: { [key: number]: Severity } = {
2323
60: Severity.Critical,
2424
};
2525

26-
export const createSentryTransformStream = (options: CosmasOptions): any => {
27-
return class SentryTransformStream extends Transform {
28-
// tslint:disable-next-line:function-name
29-
public _transform(chunk: any, _encoding: string, callback: TransformCallback) {
30-
const obj = JSON.parse(chunk);
31-
if (obj.level >= (options.sentryLevel || levels.warn)) {
32-
withScope((scope) => {
33-
scope.setLevel(PINO_TO_SENTRY[obj.level]);
34-
scope.setExtras(obj);
35-
reportToSentry(obj);
36-
});
37-
}
38-
this.push(chunk);
39-
callback();
26+
export const extendSentry = (logger: Cosmas, dsn: string | true) => {
27+
const sentry = require('@sentry/node');
28+
if (typeof dsn === 'string') {
29+
sentry.init({ dsn });
30+
}
31+
32+
const originalWrite = logger[streamSym].write;
33+
// unfortunately, this is the only place in pino, we can hook onto, where we can be sure all
34+
// the hooks, formatters and serializers are already applied
35+
logger[streamSym].write = function (s: string) {
36+
const obj = JSON.parse(s);
37+
if (obj.level >= (logger.options.sentryLevel || levels.warn)) {
38+
withScope((scope) => {
39+
scope.setLevel(PINO_TO_SENTRY[obj.level]);
40+
scope.setExtras(obj);
41+
reportToSentry(obj);
42+
});
4043
}
44+
return originalWrite.call(this, s);
4145
};
4246
};

src/streams.ts

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,6 @@ const initLoggerStreams = (
5252

5353
streams = decorateStreams(streams, getDefaultTransformStream(options));
5454

55-
if (options.sentry) {
56-
const { createSentryTransformStream } = require('./sentry');
57-
streams = decorateStreams(streams, createSentryTransformStream(options));
58-
}
59-
6055
return streams;
6156
};
6257

src/tests/sentry-mocked.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import omit = require('omit-deep');
22
import { levels } from '../levels';
33

44
let loggerFactory;
5+
let extendSentry;
56
const scope: any = {};
67
const withScope = jest.fn((fn) =>
78
fn({
@@ -44,16 +45,17 @@ describe('sentry mocked', () => {
4445
};
4546
});
4647
loggerFactory = require('../index').default;
48+
extendSentry = require('../sentry').extendSentry;
4749
});
4850
beforeEach(() => {
4951
captureException.mockReset();
5052
captureMessage.mockReset();
5153
});
5254
test('can create logger with options', () => {
5355
expect(() => loggerFactory()).not.toThrowError();
54-
expect(() => loggerFactory({ sentry: true })).not.toThrowError();
56+
expect(() => extendSentry(loggerFactory, true)).not.toThrowError();
5557
expect(init).not.toHaveBeenCalled();
56-
expect(() => loggerFactory({ sentry: 'dummy' })).not.toThrowError();
58+
expect(() => extendSentry(loggerFactory, 'dummy')).not.toThrowError();
5759
expect(init.mock.calls[0]).toMatchInlineSnapshot(`
5860
Array [
5961
Object {
@@ -71,6 +73,7 @@ describe('sentry mocked', () => {
7173
sentry: 'DSN',
7274
sentryLevel: levels.fatal,
7375
});
76+
extendSentry(logger);
7477
captureMessage.mockImplementation(createCapture(resolve));
7578
// expect to trigger only fatal
7679
logger.trace('trace');
@@ -110,6 +113,7 @@ Object {
110113
const logger = loggerFactory({
111114
sentry: 'DSN',
112115
});
116+
extendSentry(logger);
113117
captureException.mockReset();
114118
captureException.mockImplementation(createCapture(resolve));
115119
logger.error(new Error());

src/tests/sentry.test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ import loggerFactory from '../index';
33
describe('sentry not available', () => {
44
beforeAll(() => {
55
jest.mock('@sentry/node', () => {
6-
throw new Error("Cannot find module '@sentry/node' from 'index.ts'");
6+
throw new Error("Cannot find module '@sentry/node' from 'sentry.ts'");
77
});
88
});
99
test('without sentry lib works by default, but crashes on provided', () => {
1010
expect(() => loggerFactory()).not.toThrowError();
11-
expect(() => loggerFactory({ sentry: 'DSN' })).toThrowErrorMatchingInlineSnapshot(
12-
`"Cannot find module '@sentry/node' from 'index.ts'"`
13-
);
11+
expect(() => {
12+
const extendSentry = require('../sentry').extendSentry;
13+
extendSentry(loggerFactory);
14+
}).toThrowErrorMatchingInlineSnapshot(`"Cannot find module '@sentry/node' from 'sentry.ts'"`);
1415
});
1516
});

0 commit comments

Comments
 (0)