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

Commit 86a621b

Browse files
authored
Merge pull request #15 from AckeeCZ/change/logger-name-nonpretty
Add logger name to non-pretty loggers
2 parents e81f4c3 + 25a7a0f commit 86a621b

6 files changed

Lines changed: 93 additions & 24 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
- lint issue due to changes in tslint 5.12.0
55
- stream write function type
66
- pretty streams created only when needed
7+
- logger name in pretty loggers
8+
79

810
### Added
911
- coveralls integration
12+
- automatic logger name in non-pretty loggers
1013

1114
## Changed
1215
- refactoring of express handlers

jest.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ module.exports = {
88
collectCoverage: true,
99
// testURL because of problems with jsdom https://github.com/jsdom/jsdom/issues/2304
1010
testURL: "http://localhost",
11+
setupFilesAfterEnv: ['jest-extended'],
1112
};

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,15 @@
5757
"coveralls": "^3.0.2",
5858
"express": "^4.16.3",
5959
"husky": "^0.14.3",
60-
"jest": "^23.6.0",
60+
"jest": "^24.1.0",
61+
"jest-extended": "^0.11.1",
6162
"lint-staged": "^7.3.0",
6263
"npm-check": "^5.9.0",
6364
"prettier": "^1.14.3",
6465
"supertest": "^3.1.0",
65-
"ts-jest": "^23.10.4",
66+
"ts-jest": "^24.0.0",
6667
"tslint": "^5.11.0",
6768
"tslint-config-ackee": "^0.2.5",
68-
"typescript": "^3.1.3"
69+
"typescript": "^3.3.3333"
6970
}
7071
}

src/index.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,14 @@ const maxLevelWrite: pino.WriteFn = function(this: any, data: object): void {
4949
}
5050
};
5151

52-
const defaultLogger = (options: AckeeLoggerOptions = {}): AckeeLogger => {
52+
const defaultLogger = (options: AckeeLoggerOptions & { loggerName?: string } = {}): AckeeLogger => {
5353
serializers.disablePaths(options.disableFields);
5454
serializers.enablePaths(options.enableFields);
5555

5656
const isTesting = process.env.NODE_ENV === 'test';
5757
const defaultLevel: Level = options.defaultLevel || (isTesting ? 'silent' : 'debug');
58-
const streams = initLoggerStreams(defaultLevel, options);
58+
const messageKey = options.pretty ? 'msg' : 'message'; // "message" is the best option for Google Stackdriver,
59+
const streams = initLoggerStreams(defaultLevel, Object.assign({}, options, { messageKey }));
5960

6061
if (!options.ignoredHttpMethods) {
6162
options.ignoredHttpMethods = ['OPTIONS'];
@@ -66,9 +67,9 @@ const defaultLogger = (options: AckeeLoggerOptions = {}): AckeeLogger => {
6667
Object.assign(
6768
{},
6869
{
70+
messageKey,
6971
base: {},
7072
level: defaultLevel,
71-
messageKey: options.pretty ? 'msg' : 'message', // "message" is the best option for Google Stackdriver,
7273
serializers: serializers.serializers,
7374
timestamp: false,
7475
},
@@ -93,32 +94,34 @@ const defaultLogger = (options: AckeeLoggerOptions = {}): AckeeLogger => {
9394
};
9495

9596
let rootLogger: AckeeLogger;
97+
let rootOptions: AckeeLoggerOptions;
9698

9799
const parseLoggerData = (data: string | AckeeLoggerOptions = {}) => {
98-
let moduleName: string | undefined;
100+
let loggerName: string | undefined;
99101
let options: AckeeLoggerOptions = {};
100102
if (data) {
101103
if (isString(data)) {
102-
moduleName = data as string;
104+
loggerName = data;
103105
} else if (isObject(data)) {
104-
options = data as AckeeLoggerOptions;
106+
options = data;
105107
} else {
106108
throw new TypeError(`Invalid argument of type ${typeof data}`);
107109
}
108110
}
109-
return { moduleName, options };
111+
return { loggerName, options };
110112
};
111113

112114
const loggerFactory = (data: string | AckeeLoggerOptions = {}): AckeeLogger => {
113-
const { moduleName, options } = parseLoggerData(data);
115+
const { loggerName, options } = parseLoggerData(data);
114116

115117
if (!rootLogger) {
116118
rootLogger = defaultLogger(options);
119+
rootOptions = options;
117120
}
118-
if (!moduleName) {
121+
if (!loggerName) {
119122
return rootLogger;
120123
}
121-
return (rootLogger.child({ name: moduleName }) as any) as AckeeLogger;
124+
return defaultLogger(Object.assign({ loggerName }, rootOptions));
122125
};
123126

124127
const factoryProxy = new Proxy(loggerFactory, {

src/streams.ts

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,33 @@ import { AckeeLoggerOptions, AckeeLoggerStream } from './interfaces';
66
import { levels } from './levels';
77
import { StackDriverFormatStream } from './stackdriver';
88

9+
const isString = (x: any) => typeof x === 'string' || x instanceof String;
10+
911
const pkgJson = JSON.parse(fs.readFileSync(path.resolve(path.join(__dirname, '..', 'package.json')), 'utf8'));
1012

11-
class DefaultTransformStream extends Transform {
12-
// tslint:disable-next-line:function-name
13-
public _transform(chunk: any, _encoding: string, callback: (error?: Error | undefined, data?: any) => void) {
14-
const obj = JSON.parse(chunk);
15-
obj.pkgVersion = pkgJson.version;
13+
const getDefaultTransformStream = (options: AckeeLoggerOptions & { messageKey: string; loggerName?: string }) => {
14+
class DefaultTransformStream extends Transform {
15+
// tslint:disable-next-line:function-name
16+
public _transform(chunk: any, _encoding: string, callback: (error?: Error | undefined, data?: any) => void) {
17+
const obj = JSON.parse(chunk);
18+
obj.pkgVersion = pkgJson.version;
19+
const loggerName = options.loggerName;
20+
if (options.pretty) {
21+
obj['name\0'] = obj.name; // add null character so that it is not interpreted by pino-pretty but still visible to user unchanged
22+
delete obj.name;
23+
if (loggerName) {
24+
obj.name = loggerName;
25+
}
26+
} else if (obj[options.messageKey] && isString(obj[options.messageKey]) && loggerName) {
27+
obj[options.messageKey] = `[${loggerName}] ${obj[options.messageKey]}`;
28+
}
1629

17-
this.push(`${JSON.stringify(obj)}\n`);
18-
callback();
30+
this.push(`${JSON.stringify(obj)}\n`);
31+
callback();
32+
}
1933
}
20-
}
34+
return DefaultTransformStream;
35+
};
2136

2237
const decorateStreams = <T extends Transform>(streams: AckeeLoggerStream[], streamClass: new () => T) => {
2338
return streams.map(stream => {
@@ -31,7 +46,10 @@ const decorateStreams = <T extends Transform>(streams: AckeeLoggerStream[], stre
3146
});
3247
};
3348

34-
const initLoggerStreams = (defaultLevel: pino.LevelWithSilent, options: AckeeLoggerOptions = {}) => {
49+
const initLoggerStreams = (
50+
defaultLevel: pino.LevelWithSilent,
51+
options: AckeeLoggerOptions & { messageKey: string; loggerName?: string }
52+
) => {
3553
let streams: AckeeLoggerStream[];
3654
if (options.streams) {
3755
streams = options.streams;
@@ -54,7 +72,7 @@ const initLoggerStreams = (defaultLevel: pino.LevelWithSilent, options: AckeeLog
5472
streams = decorateStreams(streams, StackDriverFormatStream);
5573
}
5674

57-
streams = decorateStreams(streams, DefaultTransformStream);
75+
streams = decorateStreams(streams, getDefaultTransformStream(options));
5876

5977
return streams;
6078
};

src/tests/index.test.ts

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as express from 'express';
2+
import 'jest-extended';
23
import { Writable } from 'stream';
34
import * as supertest from 'supertest';
45
import { levels } from '../levels';
@@ -59,7 +60,7 @@ test('child logger has warning level', () =>
5960
loggerFactory({
6061
streams: [
6162
testWriteStream(resolve, json => {
62-
expect(json.message).toBe('Hello');
63+
expect(json.message).toContain('Hello');
6364
expect(json.level).toBe(levels.warn);
6465
}),
6566
],
@@ -182,3 +183,45 @@ test('silent stream does not write', () => {
182183
logger.fatal('Hello');
183184
expect(loggerWrites).not.toBeCalled();
184185
});
186+
187+
const exampleMessages = [
188+
{ type: 'simple', logData: 'Hello' },
189+
{ type: 'message-key', logData: { message: 'You gotta do, what you gotta do' } },
190+
{ type: 'msg-key', logData: { message: 'Mirror, mirror, on the wall' } },
191+
];
192+
193+
exampleMessages.forEach(data => {
194+
test(`logger name is shown in non-pretty ${data.type} message`, () =>
195+
new Promise(resolve => {
196+
const loggerName = 'database';
197+
loggerFactory({
198+
pretty: false,
199+
streams: [
200+
testWriteStream(resolve, json => {
201+
expect(json.message).toStartWith(`[${loggerName}] `);
202+
}),
203+
],
204+
});
205+
const logger = loggerFactory(loggerName);
206+
207+
logger.fatal(data.logData);
208+
}));
209+
});
210+
211+
exampleMessages.forEach(data => {
212+
test(`logger name is propagated to pretty object with ${data.type} message`, () =>
213+
new Promise(resolve => {
214+
const loggerName = 'database';
215+
loggerFactory({
216+
pretty: true,
217+
streams: [
218+
testWriteStream(resolve, json => {
219+
expect(json.name).toEqual(loggerName);
220+
}),
221+
],
222+
});
223+
const logger = loggerFactory(loggerName);
224+
225+
logger.fatal(data.logData);
226+
}));
227+
});

0 commit comments

Comments
 (0)