Skip to content

Commit baf8e42

Browse files
author
esysuser
committed
feat(configure): add ability to configure formatter and output
1 parent feb7083 commit baf8e42

File tree

11 files changed

+3532
-2707
lines changed

11 files changed

+3532
-2707
lines changed

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const Logger = require('./src/logger/logger');
44
const isNamespaceEnabled = require('./src/enabled/enabled');
55
const contextMiddlewareFactory = require('./src/context-middleware-factory/context-middleware-factory');
6+
const formatter = require('./src/formatter');
67

78
/**
89
* @param namespace
@@ -23,5 +24,9 @@ logFactory.getKoaMiddleware = contextMiddlewareFactory.getKoaMiddleware.bind(con
2324
logFactory.getExpressMiddleware = contextMiddlewareFactory.getExpressMiddleware.bind(contextMiddlewareFactory);
2425
logFactory.getMiddleware = logFactory.getKoaMiddleware;
2526
logFactory.setOnContext = contextMiddlewareFactory.setOnContext.bind(contextMiddlewareFactory);
27+
logFactory.configure = function(options) {
28+
Logger.configure(options);
29+
};
30+
logFactory.formatter = formatter;
2631

2732
module.exports = logFactory;

package-lock.json

Lines changed: 3359 additions & 2671 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/formatter/debug.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
'use strict';
2+
3+
const ColorName = require('../output/color-name/color-name');
4+
const stringifyLevel = require('../output/stringify-level/stringify-level');
5+
const formatTime = require('../output/format-time/format-time');
6+
const formatBody = require('../output/format-body/format-body');
7+
8+
module.exports = function(log) {
9+
return [
10+
ColorName.addColor(log.name),
11+
stringifyLevel(log.level),
12+
formatTime.elapsedTime(log.time),
13+
formatBody(log)
14+
].join(' ');
15+
};

src/formatter/debug.spec.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
3+
const debug = require('./debug');
4+
const ColorName = require('../output/color-name/color-name');
5+
6+
describe('debug formatter', function() {
7+
afterEach(function () {
8+
ColorName.reset();
9+
});
10+
11+
it('should format line', function() {
12+
const logLine = { level: 10, time: new Date().toISOString(), name: 'redis', random: 15 };
13+
14+
expect(debug(logLine)).to.eql('\u001b[36mredis\u001b[39m TRACE +0ms random=15');
15+
});
16+
});

src/formatter/index.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
'use strict';
2+
3+
const jsonFormatter = require('./json');
4+
const debugFormatter = require('./debug');
5+
6+
module.exports = {
7+
json: jsonFormatter,
8+
debug: debugFormatter
9+
};

src/formatter/json.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
module.exports = function(log) {
4+
return JSON.stringify(log);
5+
};

src/logger/logger.js

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ const config = require('../config');
44
const continuationLocalStorage = require('cls-hooked');
55
const STACK_TRACE_LIMIT = 4000;
66
const Timer = require('../timer/timer');
7+
const jsonFormatter = require('../formatter/json');
8+
const consoleOutput = require('../output/console');
9+
const allowedKeys = ['output', 'formatter'];
710

811
const getContextStorage = function() {
912
const contextNamespace = continuationLocalStorage.getNamespace('session');
10-
1113
if (contextNamespace && contextNamespace.active) {
1214
const { id, _ns_name, ...contextData } = contextNamespace.active;
1315
return contextData;
@@ -16,31 +18,25 @@ const getContextStorage = function() {
1618
return {};
1719
};
1820

19-
const logMethodFactory = function(level) {
20-
return function(action, data) {
21-
if (!this._enabled) {
22-
return;
23-
}
24-
25-
console.log(JSON.stringify(Object.assign(
26-
{
27-
name: this._namespace,
28-
action: action,
29-
level: config.levels[level].number,
30-
time: new Date().toISOString()
31-
},
32-
getContextStorage(),
33-
data
34-
)));
35-
}
36-
};
37-
3821
class Logger {
3922
constructor(namespace, enabled) {
4023
this._namespace = namespace;
4124
this._enabled = enabled;
4225
}
4326

27+
static configure(options = {}) {
28+
this._validate(options);
29+
Object.assign(Logger.config, options);
30+
}
31+
32+
static _validate(options) {
33+
Object.keys(options).forEach(key => {
34+
if (!allowedKeys.includes(key)) {
35+
throw new Error('Only the following keys are allowed: formatter, output')
36+
}
37+
});
38+
}
39+
4440
isEnabled() {
4541
return this._enabled;
4642
}
@@ -64,6 +60,34 @@ class Logger {
6460
}
6561
}
6662

63+
Logger.config = {
64+
formatter: jsonFormatter,
65+
output: consoleOutput
66+
};
67+
68+
const logMethodFactory = function(level) {
69+
return function(action, data) {
70+
if (!this._enabled) {
71+
return;
72+
}
73+
74+
const dataToLog = Object.assign(
75+
{
76+
name: this._namespace,
77+
action: action,
78+
level: config.levels[level].number,
79+
time: new Date().toISOString()
80+
},
81+
getContextStorage(),
82+
data
83+
);
84+
85+
Logger.config.output(
86+
Logger.config.formatter(dataToLog)
87+
);
88+
}
89+
};
90+
6791
Logger.prototype.trace = logMethodFactory('trace');
6892
Logger.prototype.debug = logMethodFactory('debug');
6993
Logger.prototype.info = logMethodFactory('info');

src/logger/logger.spec.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
const Logger = require('./logger');
44
const continuationLocalStorage = require('cls-hooked');
5+
const jsonFormatter = require('../formatter/json');
6+
const consoleOutput = require('../output/console');
57

68
describe('Logger', function() {
79
let logger;
@@ -11,6 +13,13 @@ describe('Logger', function() {
1113
this.sandbox.stub(console, 'log');
1214
});
1315

16+
afterEach(function() {
17+
Logger.configure({
18+
formatter: jsonFormatter,
19+
output: consoleOutput
20+
});
21+
});
22+
1423
it('should call log info method when enabled', function() {
1524
logger.info('wedidit', { details: 'forever' });
1625

@@ -67,4 +76,41 @@ describe('Logger', function() {
6776
expect(logArguments.error_stack).to.eql(error.stack);
6877
expect(logArguments.error_message).to.eql(error.message);
6978
});
79+
80+
describe('#configure', function() {
81+
it('should change format method', function() {
82+
const formattedOutput = '{"my":"method"}';
83+
const formatterStub = this.sandbox.stub();
84+
formatterStub.returns(formattedOutput);
85+
86+
Logger.configure({
87+
formatter: formatterStub
88+
});
89+
logger.info('hi');
90+
91+
expect(formatterStub).to.have.been.called;
92+
expect(console.log).to.have.been.calledWith(formattedOutput);
93+
});
94+
95+
it('should change output method', function() {
96+
const outputStub = this.sandbox.stub();
97+
Logger.configure({
98+
output: outputStub
99+
});
100+
logger.info('hi');
101+
102+
expect(outputStub).to.have.been.called;
103+
});
104+
105+
it('should throw error on invalid config', function() {
106+
try {
107+
Logger.configure({
108+
invalid: true
109+
});
110+
throw new Error('should throw');
111+
} catch(e) {
112+
expect(e.message).to.eql('Only the following keys are allowed: formatter, output');
113+
}
114+
});
115+
});
70116
});

src/output/color-name/color-name.js

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
'use strict';
22

33
const chalk = require('chalk');
4-
54
const colors = ['cyan', 'magenta', 'grey', 'blue', 'green', 'yellow', 'white', 'red'];
6-
const names = {};
7-
let colorCounter = 0;
85

9-
module.exports = function colorName(name) {
10-
if (!names[name]) {
11-
names[name] = { color: colorCounter % colors.length };
12-
colorCounter++;
6+
class ColorName {
7+
static addColor(name) {
8+
if (!this.names[name]) {
9+
this.names[name] = { color: this.counter % colors.length };
10+
this.counter++;
11+
}
12+
13+
const color = colors[this.names[name].color];
14+
return chalk[color](name);
15+
}
16+
17+
static reset() {
18+
this.counter = 0;
19+
this.names = {};
1320
}
21+
}
1422

15-
const color = colors[names[name].color];
16-
return chalk[color](name);
17-
};
23+
ColorName.counter = 0;
24+
ColorName.names = {};
25+
ColorName.colors = colors;
1826

19-
module.exports.colors = colors;
27+
module.exports = ColorName;

src/output/color-name/color-name.spec.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
'use strict';
22

3-
const colorName = require('./color-name');
3+
const ColorName = require('./color-name');
44

55
describe('colorName', function() {
6+
afterEach(function () {
7+
ColorName.reset();
8+
});
9+
610
it('should pick the first color for the name', function() {
7-
expect(colorName('mongo')).to.eql('\u001b[36mmongo\u001b[39m');
11+
expect(ColorName.addColor('mongo')).to.eql('\u001b[36mmongo\u001b[39m');
812
});
913

1014
it('should pick the same color for same name', function() {
11-
expect(colorName('mongo')).to.eql(colorName('mongo'));
15+
expect(ColorName.addColor('mongo')).to.eql(ColorName.addColor('mongo'));
1216
});
1317

1418
it('should add different colors for different names', function() {
15-
const firstName = colorName('mongo');
16-
const secondName = colorName('redis');
19+
const firstName = ColorName.addColor('mongo');
20+
const secondName = ColorName.addColor('redis');
1721

1822
expect(secondName.replace('redis', 'mongo')).not.to.eql(firstName);
1923
});

src/output/console.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
'use strict';
2+
3+
module.exports = function(formattedLog) {
4+
console.log(formattedLog);
5+
};

0 commit comments

Comments
 (0)