UNPKG

2.99 kBJavaScriptView Raw
1'use strict';
2
3const noop = () => {};
4const levels = Symbol('valid log levels');
5const instance = Symbol('a log instance');
6
7module.exports = class MethodFactory {
8 constructor(logger) {
9 this[instance] = logger;
10 this[levels] = {
11 TRACE: 0,
12 DEBUG: 1,
13 INFO: 2,
14 WARN: 3,
15 ERROR: 4,
16 SILENT: 5
17 };
18 }
19
20 get levels() {
21 return this[levels];
22 }
23
24 get logger() {
25 return this[instance];
26 }
27
28 set logger(logger) {
29 this[instance] = logger;
30 }
31
32 get methods() {
33 return Object.keys(this.levels)
34 .map(key => key.toLowerCase())
35 .filter(key => key !== 'silent');
36 }
37
38 // eslint-disable-next-line class-methods-use-this
39 bindMethod(obj, methodName) {
40 const method = obj[methodName];
41 if (typeof method.bind === 'function') {
42 return method.bind(obj);
43 }
44
45 try {
46 return Function.prototype.bind.call(method, obj);
47 } catch (e) {
48 // Missing bind shim or IE8 + Modernizr, fallback to wrapping
49 return function result() {
50 // eslint-disable-next-line prefer-rest-params
51 return Function.prototype.apply.apply(method, [obj, arguments]);
52 };
53 }
54 }
55
56 distillLevel(level) {
57 let result = level;
58
59 if (typeof result === 'string' && typeof this.levels[result.toUpperCase()] !== 'undefined') {
60 result = this.levels[result.toUpperCase()];
61 }
62
63 if (this.levelValid(result)) {
64 return result;
65 }
66 }
67
68 levelValid(level) {
69 if (typeof level === 'number' && level >= 0 && level <= this.levels.SILENT) {
70 return true;
71 }
72
73 return false;
74 }
75
76 /**
77 * Build the best logging method possible for this env
78 * Wherever possible we want to bind, not wrap, to preserve stack traces.
79 * Since we're targeting modern browsers, there's no need to wait for the
80 * console to become available.
81 */
82 // eslint-disable-next-line class-methods-use-this
83 make(methodName) {
84 if (methodName === 'debug') {
85 methodName = 'log';
86 }
87
88 /* eslint-disable no-console */
89 if (typeof console[methodName] !== 'undefined') {
90 return this.bindMethod(console, methodName);
91 } else if (typeof console.log !== 'undefined') {
92 return this.bindMethod(console, 'log');
93 }
94
95 /* eslint-enable no-console */
96 return noop;
97 }
98
99 replaceMethods(logLevel) {
100 const level = this.distillLevel(logLevel);
101
102 if (level == null) {
103 throw new Error(`loglevelnext: replaceMethods() called with invalid level: ${logLevel}`);
104 }
105
106 if (!this.logger || this.logger.type !== 'LogLevel') {
107 throw new TypeError('loglevelnext: Logger is undefined or invalid. Please specify a valid Logger instance.');
108 }
109
110 for (const methodName of this.methods) {
111 const { [methodName.toUpperCase()]: methodLevel } = this.levels;
112
113 this.logger[methodName] = (methodLevel < level) ? noop : this.make(methodName);
114 }
115
116 // Define log.log as an alias for log.debug
117 this.logger.log = this.logger.debug;
118 }
119};