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

Commit

Permalink
Merge 09086dc into 5a89e88
Browse files Browse the repository at this point in the history
  • Loading branch information
vlasy authored Oct 23, 2019
2 parents 5a89e88 + 09086dc commit d2c3a63
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 31 deletions.
37 changes: 21 additions & 16 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,20 @@ export interface AckeeLogger extends PinoLogger {
express: AckeeLoggerExpressMiddleware;
expressError: ErrorRequestHandler;
stream: Writable;
(childName: string): any;
}

export interface AckeeLoggerFactory extends AckeeLogger {
(data: string | AckeeLoggerOptions): AckeeLogger;
(data?: string | AckeeLoggerOptions): AckeeLogger;
}

const makeCallable = <T extends object, F extends (...args: any[]) => any>(obj: T, fun: F): T & F =>
new Proxy(fun as any, {
get: (_target, key) => (obj as any)[key],
});

const objEmpty = (obj: object) => Object.keys(obj).length === 0;

// This is a custom slightly edited version of pino-multistream's write method, which adds support for maximum log level
// The original version was pino-multistream 4.2.0 (commit bf7941f) - https://github.com/pinojs/pino-multi-stream/blob/bf7941f77661b6c14dd40840ff4a4db6897f08eb/multistream.js#L43
const maxLevelWrite: pino.WriteFn = function(this: any, data: object): void {
Expand Down Expand Up @@ -95,9 +103,6 @@ const defaultLogger = (options: AckeeLoggerOptions & { loggerName?: string } = {
});
};

let rootLogger: AckeeLogger;
let rootOptions: AckeeLoggerOptions;

const parseLoggerData = (data: string | AckeeLoggerOptions = {}) => {
let loggerName: string | undefined;
let options: AckeeLoggerOptions = {};
Expand All @@ -113,21 +118,21 @@ const parseLoggerData = (data: string | AckeeLoggerOptions = {}) => {
return { loggerName, options };
};

const loggerFactory = (data: string | AckeeLoggerOptions = {}): AckeeLogger => {
const loggerFactory = (data: string | AckeeLoggerOptions = {}, loggerOptions: AckeeLoggerOptions = {}): AckeeLogger => {
// console.log('Factory called');
const { loggerName, options } = parseLoggerData(data);
loggerOptions = objEmpty(options) ? loggerOptions : options;
const logger = defaultLogger(Object.assign({ loggerName }, loggerOptions));

if (!rootLogger) {
rootLogger = defaultLogger(options);
rootOptions = options;
}
if (!loggerName) {
return rootLogger;
}
return defaultLogger(Object.assign({ loggerName }, rootOptions));
const loggerProxy = makeCallable(logger, (childName: string) => {
const childLoggerName = [loggerName, childName].join('');
// console.log('Creating child', childName);
const childOptions = loggerOptions;
return loggerFactory(childLoggerName, childOptions);
});
return loggerProxy;
};

const factoryProxy = new Proxy(loggerFactory, {
get: (target, key) => (target() as any)[key],
}) as AckeeLoggerFactory;
const factoryProxy = makeCallable(loggerFactory(), loggerFactory);

export default factoryProxy;
72 changes: 57 additions & 15 deletions src/tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import 'jest-extended';
import isString = require('lodash.isstring');
import { Writable } from 'stream';
import loggerFactory from '..';
import { levels } from '../levels';

let loggerFactory;

beforeEach(() => {
jest.resetModules();
loggerFactory = require('..').default;
});

test('can create default logger', () => {
const logger = loggerFactory();
expect(logger).toBeDefined();
Expand All @@ -17,6 +12,13 @@ test('can create default logger', () => {
test('can create named logger', () => {
const logger = loggerFactory('myApp');
expect(logger).toBeDefined();
expect((logger.options as any).loggerName).toBe('myApp');
});

test.skip('can create logger with options', () => {
const logger = loggerFactory({ pretty: true });
expect(logger).toBeDefined();
expect(logger.options.pretty).toBe(true);
});

const testWriteStream = (resolve, assert) => ({
Expand Down Expand Up @@ -55,15 +57,15 @@ test('can use warning level', () =>

test('child logger has warning level', () =>
new Promise((resolve, reject) => {
loggerFactory({
const rootLogger = loggerFactory({
streams: [
testWriteStream(resolve, json => {
expect(json.message).toContain('Hello');
expect(json.level).toBe(levels.warn);
}),
],
});
const childLogger = loggerFactory('child');
const childLogger = rootLogger('child');

childLogger.warning('Hello');
}));
Expand Down Expand Up @@ -126,34 +128,74 @@ exampleMessages.forEach(data => {
test(`logger name is shown in non-pretty ${data.type} message`, () =>
new Promise(resolve => {
const loggerName = 'database';
loggerFactory({
const rootLogger = loggerFactory({
pretty: false,
streams: [
testWriteStream(resolve, json => {
expect(json.message).toStartWith(`[${loggerName}] `);
}),
],
});
const logger = loggerFactory(loggerName);
const logger = rootLogger(loggerName);

logger.fatal(data.logData);
if (isString(data.logData)) {
logger.fatal(data.logData);
} else {
logger.fatal(data.logData, 'Data');
}
}));
});

exampleMessages.forEach(data => {
test(`logger name is propagated to pretty object with ${data.type} message`, () =>
new Promise(resolve => {
const loggerName = 'database';
loggerFactory({
const rootLogger = loggerFactory({
pretty: true,
streams: [
testWriteStream(resolve, json => {
expect(json.name).toEqual(loggerName);
}),
],
});
const logger = loggerFactory(loggerName);
const logger = rootLogger(loggerName);

logger.fatal(data.logData);
if (isString(data.logData)) {
logger.fatal(data.logData);
} else {
logger.fatal(data.logData, 'Data');
}
}));
});

test('multiple logger configs are not affected', () => {
const primaryLogger = loggerFactory({ pretty: true, ignoredHttpMethods: ['POST'] });
const secondaryLogger = loggerFactory({ pretty: false, ignoredHttpMethods: ['GET'] });

expect(primaryLogger.options.pretty).toBe(true);
expect(primaryLogger.options.ignoredHttpMethods).toIncludeSameMembers(['POST']);
expect(secondaryLogger.options.pretty).toBe(false);
expect(secondaryLogger.options.ignoredHttpMethods).toIncludeSameMembers(['GET']);
});

test('Child logger takes parent config', () => {
const logger = loggerFactory({ pretty: true });
const childLogger = logger('child');

expect(childLogger.options.pretty).toBe(true);
});

test('Child logger inherits parent name', () => {
const logger = loggerFactory('parent', { pretty: true });
const childLogger = logger('child');

expect(childLogger.options.loggerName).toBe('parentchild');
});

test('Child logger can create another child', () => {
const logger = loggerFactory('parent', { pretty: true });
const childLogger = logger('child');
const kid = childLogger('grandkid');

expect(kid.options.loggerName).toBe('parentchildgrandkid');
});

0 comments on commit d2c3a63

Please sign in to comment.