-
Notifications
You must be signed in to change notification settings - Fork 108
/
Copy pathlogger.js
290 lines (246 loc) · 9.38 KB
/
logger.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*!
* js-logger - http://github.com/jonnyreeves/js-logger
* Jonny Reeves, http://jonnyreeves.co.uk/
* js-logger may be freely distributed under the MIT license.
*/
(function (global) {
"use strict";
// Top level module for the global, static logger instance.
var Logger = {};
// For those that are at home that are keeping score.
Logger.VERSION = "1.6.1";
// Function which handles all incoming log messages.
var logHandler;
// Map of ContextualLogger instances by name; used by Logger.get() to return the same named instance.
var contextualLoggersByNameMap = {};
// Polyfill for ES5's Function.bind.
var bind = function (scope, func) {
return function () {
return func.apply(scope, arguments);
};
};
// Super exciting object merger-matron 9000 adding another 100 bytes to your download.
var merge = function () {
var args = arguments,
target = args[0],
key,
i;
for (i = 1; i < args.length; i++) {
for (key in args[i]) {
if (!(key in target) && args[i].hasOwnProperty(key)) {
target[key] = args[i][key];
}
}
}
return target;
};
// Helper to define a logging level object; helps with optimisation.
var defineLogLevel = function (value, name) {
return { value: value, name: name };
};
// Predefined logging levels.
Logger.TRACE = defineLogLevel(1, "TRACE");
Logger.DEBUG = defineLogLevel(2, "DEBUG");
Logger.INFO = defineLogLevel(3, "INFO");
Logger.TIME = defineLogLevel(4, "TIME");
Logger.WARN = defineLogLevel(5, "WARN");
Logger.ERROR = defineLogLevel(8, "ERROR");
Logger.OFF = defineLogLevel(99, "OFF");
// Inner class which performs the bulk of the work; ContextualLogger instances can be configured independently
// of each other.
var ContextualLogger = function (defaultContext) {
this.context = defaultContext;
this.setLevel(defaultContext.filterLevel);
this.log = this.info; // Convenience alias.
};
ContextualLogger.prototype = {
// Changes the current logging level for the logging instance.
setLevel: function (newLevel) {
// Ensure the supplied Level object looks valid.
if (newLevel && "value" in newLevel) {
this.context.filterLevel = newLevel;
}
},
// Gets the current logging level for the logging instance
getLevel: function () {
return this.context.filterLevel;
},
// Is the logger configured to output messages at the supplied level?
enabledFor: function (lvl) {
var filterLevel = this.context.filterLevel;
return lvl.value >= filterLevel.value;
},
trace: function () {
this.invoke(Logger.TRACE, arguments);
},
debug: function () {
this.invoke(Logger.DEBUG, arguments);
},
info: function () {
this.invoke(Logger.INFO, arguments);
},
warn: function () {
this.invoke(Logger.WARN, arguments);
},
error: function () {
this.invoke(Logger.ERROR, arguments);
},
time: function (label) {
if (typeof label === "string" && label.length > 0) {
this.invoke(Logger.TIME, [label, "start"]);
}
},
timeEnd: function (label) {
if (typeof label === "string" && label.length > 0) {
this.invoke(Logger.TIME, [label, "end"]);
}
},
// Invokes the logger callback if it's not being filtered.
invoke: function (level, msgArgs) {
if (logHandler && this.enabledFor(level)) {
logHandler(msgArgs, merge({ level: level }, this.context));
}
},
};
// Protected instance which all calls to the to level `Logger` module will be routed through.
var globalLogger = new ContextualLogger({ filterLevel: Logger.OFF });
// Configure the global Logger instance.
(function () {
// Shortcut for optimisers.
var L = Logger;
L.enabledFor = bind(globalLogger, globalLogger.enabledFor);
L.trace = bind(globalLogger, globalLogger.trace);
L.debug = bind(globalLogger, globalLogger.debug);
L.time = bind(globalLogger, globalLogger.time);
L.timeEnd = bind(globalLogger, globalLogger.timeEnd);
L.info = bind(globalLogger, globalLogger.info);
L.warn = bind(globalLogger, globalLogger.warn);
L.error = bind(globalLogger, globalLogger.error);
// Don't forget the convenience alias!
L.log = L.info;
})();
// Set the global logging handler. The supplied function should expect two arguments, the first being an arguments
// object with the supplied log messages and the second being a context object which contains a hash of stateful
// parameters which the logging function can consume.
Logger.setHandler = function (func) {
logHandler = func;
};
// Sets the global logging filter level which applies to *all* previously registered, and future Logger instances.
// (note that named loggers (retrieved via `Logger.get`) can be configured independently if required).
Logger.setLevel = function (level) {
// Set the globalLogger's level.
globalLogger.setLevel(level);
// Apply this level to all registered contextual loggers.
for (var key in contextualLoggersByNameMap) {
if (contextualLoggersByNameMap.hasOwnProperty(key)) {
contextualLoggersByNameMap[key].setLevel(level);
}
}
};
// Gets the global logging filter level
Logger.getLevel = function () {
return globalLogger.getLevel();
};
// Retrieve a ContextualLogger instance. Note that named loggers automatically inherit the global logger's level,
// default context and log handler.
Logger.get = function (name) {
// All logger instances are cached so they can be configured ahead of use.
return (
contextualLoggersByNameMap[name] ||
(contextualLoggersByNameMap[name] = new ContextualLogger(
merge({ name: name }, globalLogger.context)
))
);
};
// CreateDefaultHandler returns a handler function which can be passed to `Logger.setHandler()` which will
// write to the window's console object (if present); the optional options object can be used to customise the
// formatter used to format each log message.
Logger.createDefaultHandler = function (options) {
options = options || {};
options.formatter =
options.formatter ||
function defaultMessageFormatter(messages, context) {
// Prepend the logger's name to the log message for easy identification.
if (context.name) {
messages.unshift("[" + context.name + "]");
}
};
// Map of timestamps by timer labels used to track `#time` and `#timeEnd()` invocations in environments
// that don't offer a native console method.
var timerStartTimeByLabelMap = {};
// Support for IE8+ (and other, slightly more sane environments)
var invokeConsoleMethod = function (hdlr, messages) {
Function.prototype.apply.call(hdlr, console, messages);
};
// Check for the presence of a logger.
if (typeof console === "undefined") {
return function () {
/* no console */
};
}
return function (messages, context) {
// Convert arguments object to Array.
messages = Array.prototype.slice.call(messages);
var hdlr = console.log;
var timerLabel;
if (context.level === Logger.TIME) {
timerLabel =
(context.name ? "[" + context.name + "] " : "") + messages[0];
if (messages[1] === "start") {
if (console.time) {
console.time(timerLabel);
} else {
timerStartTimeByLabelMap[timerLabel] = new Date().getTime();
}
} else {
if (console.timeEnd) {
console.timeEnd(timerLabel);
} else {
invokeConsoleMethod(hdlr, [
timerLabel +
": " +
(new Date().getTime() - timerStartTimeByLabelMap[timerLabel]) +
"ms",
]);
}
}
} else {
// Delegate through to custom warn/error loggers if present on the console.
if (context.level === Logger.WARN && console.warn) {
hdlr = console.warn;
} else if (context.level === Logger.ERROR && console.error) {
hdlr = console.error;
} else if (context.level === Logger.INFO && console.info) {
hdlr = console.info;
} else if (context.level === Logger.DEBUG && console.debug) {
hdlr = console.debug;
} else if (context.level === Logger.TRACE && console.trace) {
hdlr = console.trace;
}
options.formatter(messages, context);
invokeConsoleMethod(hdlr, messages);
}
};
};
// Configure and example a Default implementation which writes to the `window.console` (if present). The
// `options` hash can be used to configure the default logLevel and provide a custom message formatter.
Logger.useDefaults = function (options) {
Logger.setLevel((options && options.defaultLevel) || Logger.DEBUG);
Logger.setHandler(Logger.createDefaultHandler(options));
};
// Createa an alias to useDefaults to avoid reaking a react-hooks rule.
Logger.setDefaults = Logger.useDefaults;
// Export to popular environments boilerplate.
if (typeof define === "function" && define.amd) {
define(Logger);
} else if (typeof module !== "undefined" && module.exports) {
module.exports = Logger;
} else {
Logger._prevLogger = global.Logger;
Logger.noConflict = function () {
global.Logger = Logger._prevLogger;
return Logger;
};
global.Logger = Logger;
}
})(this);