Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Contextual Logger - Propagating changes to children
diff --git a/logger.js b/logger.js
index d19e435..d2740cd 100644
--- a/logger.js
+++ b/logger.js
@@ -1,8 +1,6 @@
const pino = require('pino');
const { createNamespace } = require('cls-hooked');
-const logMethods = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
-
const logMethodHandler = {
apply(target, thisArg, argumentList) {
// eslint-disable-next-line camelcase
@@ -26,25 +24,47 @@ const logMethodHandler = {
},
};
+const childMethodHandler = {
+ apply(target, thisArg, argumentList) {
+ const { cls } = thisArg;
+
+ // eslint-disable-next-line no-use-before-define
+ return createWrapper({ cls }, target.apply(thisArg, argumentList));
+ },
+};
+
+const logMethods = ['trace', 'debug', 'info', 'warn', 'error', 'fatal'];
+const childMethod = 'child';
+
const loggerObjectHandler = {
get(target, prop) {
- if (!logMethods.includes(prop)) {
- return target[prop];
+ if (logMethods.includes(prop)) {
+ return new Proxy(target[prop], logMethodHandler);
+ }
+
+ if (prop === childMethod) {
+ return new Proxy(target[prop], childMethodHandler);
}
- return new Proxy(target[prop], logMethodHandler);
+ return target[prop];
},
};
+function createWrapper({ cls }, pinoInstance) {
+ const baseLogger = Object.assign(pinoInstance, { cls });
+
+ return new Proxy(baseLogger, loggerObjectHandler);
+}
+
let counter = 0;
function createLogger(opts, destination) {
- const baseLogger = pino(opts, destination);
const cls = createNamespace(`@@logger-${counter}`);
-
counter += 1;
- return new Proxy(Object.assign(baseLogger, { cls }), loggerObjectHandler);
+ const pinoInstance = pino(opts, destination);
+
+ return createWrapper({ cls }, pinoInstance);
}
module.exports = createLogger;
diff --git a/logger.test.js b/logger.test.js
index 155415a..c1cb970 100644
--- a/logger.test.js
+++ b/logger.test.js
@@ -100,3 +100,69 @@ test.cb(
});
}
);
+
+test.cb(`Should properly propagate cls context to child logger`, t => {
+ const stream = parseJSONStream();
+ const gen = streamToGenerator(stream);
+ const logger = createLogger({}, stream);
+
+ const msg = 'foo';
+ const clsValues = {
+ dummy: 'value',
+ another: 'another value',
+ };
+
+ const childContext = { foo: 'bar' };
+ const child = logger.child(childContext);
+
+ logger.cls.run(() => {
+ logger.cls.set('dummy', clsValues.dummy);
+ logger.cls.set('another', clsValues.another);
+ process.nextTick(async () => {
+ child.info(msg);
+
+ const entry = await gen.next().value;
+
+ t.is(entry.dummy, clsValues.dummy);
+ t.is(entry.another, clsValues.another);
+ t.is(entry.foo, childContext.foo);
+
+ t.end();
+ });
+ });
+});
+
+test.cb(`Should properly propagate cls context to grand child logger`, t => {
+ const stream = parseJSONStream();
+ const gen = streamToGenerator(stream);
+ const logger = createLogger({}, stream);
+
+ const msg = 'foo';
+ const clsValues = {
+ dummy: 'value',
+ another: 'another value',
+ };
+
+ const childContext = { foo: 'bar' };
+ const child = logger.child(childContext);
+
+ const grandChildContext = { fizz: 'buzz' };
+ const grandChild = child.child(grandChildContext);
+
+ logger.cls.run(() => {
+ logger.cls.set('dummy', clsValues.dummy);
+ logger.cls.set('another', clsValues.another);
+ process.nextTick(async () => {
+ grandChild.info(msg);
+
+ const entry = await gen.next().value;
+
+ t.is(entry.dummy, clsValues.dummy);
+ t.is(entry.another, clsValues.another);
+ t.is(entry.foo, childContext.foo);
+ t.is(entry.fizz, grandChildContext.fizz);
+
+ t.end();
+ });
+ });
+});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.