1 | /* Copyright @ 2015-present 8x8, Inc.
|
2 | *
|
3 | * Licensed under the Apache License, Version 2.0 (the "License");
|
4 | * you may not use this file except in compliance with the License.
|
5 | * You may obtain a copy of the License at
|
6 | *
|
7 | * http://www.apache.org/licenses/LICENSE-2.0
|
8 | *
|
9 | * Unless required by applicable law or agreed to in writing, software
|
10 | * distributed under the License is distributed on an "AS IS" BASIS,
|
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12 | * See the License for the specific language governing permissions and
|
13 | * limitations under the License.
|
14 | */
|
15 | /*jslint latedef:false*/
|
16 |
|
17 | /**
|
18 | * Ordered log levels.
|
19 | */
|
20 | var levels = {
|
21 | "trace": 0,
|
22 | "debug": 1,
|
23 | "info": 2,
|
24 | "log": 3,
|
25 | "warn": 4,
|
26 | "error": 5
|
27 | };
|
28 |
|
29 | /**
|
30 | * The default transport - console
|
31 | * @type LoggerTransport
|
32 | */
|
33 | Logger.consoleTransport = console;
|
34 |
|
35 | /**
|
36 | * The array which stores currently registered global transports.
|
37 | * @type {[LoggerTransport]}
|
38 | */
|
39 | var globalTransports = [ Logger.consoleTransport ];
|
40 |
|
41 | /**
|
42 | * Adds given {@link LoggerTransport} instance to the list of global
|
43 | * transports which means that it'll be used by all {@link Logger}s
|
44 | * @param {LoggerTransport} transport
|
45 | */
|
46 | Logger.addGlobalTransport = function(transport) {
|
47 | if (globalTransports.indexOf(transport) === -1) {
|
48 | globalTransports.push(transport);
|
49 | }
|
50 | };
|
51 |
|
52 | /**
|
53 | * Removes given {@link LoggerTransport} instance from the list of global
|
54 | * transports
|
55 | * @param {LoggerTransport} transport
|
56 | */
|
57 | Logger.removeGlobalTransport = function(transport) {
|
58 | var transportIdx = globalTransports.indexOf(transport);
|
59 | if (transportIdx !== -1) {
|
60 | globalTransports.splice(transportIdx, 1);
|
61 | }
|
62 | };
|
63 |
|
64 | /**
|
65 | * The global configuration options.
|
66 | */
|
67 | var globalOptions = {};
|
68 |
|
69 | /**
|
70 | * Sets global options which will be used by all loggers. Changing these works
|
71 | * even after other loggers are created.
|
72 | */
|
73 | Logger.setGlobalOptions = function(options) {
|
74 | globalOptions = options || {};
|
75 | };
|
76 |
|
77 | /**
|
78 | * Parses Error's object stack trace and extracts information about the last
|
79 | * caller before the log method was called.
|
80 | * @returns JS object with info about the caller - method name, file location,
|
81 | * line and column.
|
82 | */
|
83 | function getCallerInfo() {
|
84 | var callerInfo = {
|
85 | methodName: "",
|
86 | fileLocation: "",
|
87 | line: null,
|
88 | column: null
|
89 | };
|
90 | //gets the part of the stack without the logger wrappers
|
91 | var error = new Error();
|
92 | var stack = error.stack? error.stack.split("\n") : [];
|
93 | if(!stack || stack.length < 3) {
|
94 | return callerInfo;
|
95 | }
|
96 | var m = null;
|
97 | if(stack[3]) {
|
98 | m = stack[3].match(/\s*at\s*(.+?)\s*\((\S*)\s*:(\d*)\s*:(\d*)\)/);
|
99 | }
|
100 | if(!m || m.length <= 4) {
|
101 | //Firefox && Safari
|
102 | if(stack[2].indexOf("log@") === 0){
|
103 | //Safari
|
104 | callerInfo.methodName = stack[3].substr(0, stack[3].indexOf("@"));
|
105 | } else {
|
106 | //Firefox
|
107 | callerInfo.methodName = stack[2].substr(0, stack[2].indexOf("@"));
|
108 | }
|
109 | return callerInfo;
|
110 | }
|
111 |
|
112 | callerInfo.methodName = m[1];
|
113 | callerInfo.fileLocation = m[2];
|
114 | callerInfo.line = m[3];
|
115 | callerInfo.column = m[4];
|
116 | return callerInfo;
|
117 | }
|
118 |
|
119 | /**
|
120 | * Logs messages using the transports and level from the logger.
|
121 | * @param logger a logger instance.
|
122 | * @param level the log level of the message. See the levels variable.
|
123 | * @param arguments array with arguments that will be logged.
|
124 | */
|
125 | function log() {
|
126 | var logger = arguments[0], level = arguments[1],
|
127 | args = Array.prototype.slice.call(arguments, 2);
|
128 | if(levels[level] < logger.level) {
|
129 | return;
|
130 | }
|
131 |
|
132 | var callerInfo
|
133 | = !(logger.options.disableCallerInfo || globalOptions.disableCallerInfo) &&
|
134 | getCallerInfo();
|
135 | var transports = globalTransports.concat(logger.transports);
|
136 | for(var i = 0; i < transports.length; i++) {
|
137 | var t = transports[i];
|
138 | var l = t[level];
|
139 | if(l && typeof(l) === "function") {
|
140 | var logPrefixes = [];
|
141 |
|
142 | logPrefixes.push(new Date().toISOString());
|
143 |
|
144 | if (logger.id) {
|
145 | logPrefixes.push("[" + logger.id + "]");
|
146 | }
|
147 |
|
148 | if (callerInfo && callerInfo.methodName.length > 1) {
|
149 | logPrefixes.push("<" + callerInfo.methodName + ">: ");
|
150 | }
|
151 |
|
152 | var fullLogParts = logPrefixes.concat(args);
|
153 |
|
154 | try {
|
155 | l.bind(t).apply(t, fullLogParts);
|
156 | } catch (error) {
|
157 | // It would be nice to send the error to the logger but this could send us into an endless loop.
|
158 | // That's why we use only console for logging here.
|
159 | console.error("An error occured when trying to log with one of the available transports", error);
|
160 | }
|
161 | }
|
162 | }
|
163 | }
|
164 |
|
165 | /**
|
166 | *
|
167 | * Constructs new logger object.
|
168 | * @param level the logging level for the new logger
|
169 | * @param id optional identifier for the logger instance.
|
170 | * @param {LoggerTransport} transports optional list of handlers(objects) for
|
171 | * the logs. The handlers must support - log, warn, error, debug, info, trace.
|
172 | * @param options optional configuration file for how the logger should behave.
|
173 | * @param {boolean} options.disableCallerInfo Whether the call site of a logger
|
174 | * method invocation should be included in the log. Defaults to false, so the
|
175 | * call site will be included.
|
176 | */
|
177 | function Logger(level, id, transports, options) {
|
178 | this.id = id;
|
179 | this.options = options || {};
|
180 | this.transports = transports;
|
181 | if(!this.transports) {
|
182 | this.transports = [];
|
183 | }
|
184 | this.level = levels[level];
|
185 | var methods = Object.keys(levels);
|
186 | for(var i = 0; i < methods.length; i++){
|
187 | this[methods[i]] =
|
188 | log.bind(null, this, methods[i]);
|
189 | }
|
190 | }
|
191 |
|
192 | /**
|
193 | * Sets the log level for the logger.
|
194 | * @param level the new log level.
|
195 | */
|
196 | Logger.prototype.setLevel = function (level) {
|
197 | this.level = levels[level];
|
198 | };
|
199 | module.exports = Logger;
|
200 |
|
201 | /**
|
202 | * Enum for the supported log levels.
|
203 | */
|
204 | Logger.levels = {
|
205 | TRACE: "trace",
|
206 | DEBUG: "debug",
|
207 | INFO: "info",
|
208 | LOG: "log",
|
209 | WARN: "warn",
|
210 | ERROR: "error"
|
211 | };
|