UNPKG

7.38 kBJavaScriptView Raw
1/*
2 * Licensed under the Apache License, Version 2.0 (the "License");
3 * you may not use this file except in compliance with the License.
4 * You may obtain a copy of the License at
5 *
6 * http://www.apache.org/licenses/LICENSE-2.0
7 *
8 * Unless required by applicable law or agreed to in writing, software
9 * distributed under the License is distributed on an "AS IS" BASIS,
10 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 * See the License for the specific language governing permissions and
12 * limitations under the License.
13 */
14
15'use strict';
16
17/* eslint-disable no-console */
18/* eslint-disable no-use-before-define */
19
20const colors = require('colors/safe');
21const jsonColorize = require('json-colorizer');
22
23/**
24 * Default levels for the npm configuration.
25 * @type {Object}
26 */
27const levels = {
28 error: 0,
29 warn: 1,
30 info: 2,
31 http: 3,
32 verbose: 4,
33 debug: 5,
34 silly: 6
35};
36
37/**
38 * Default levels for the npm configuration.
39 * @type {Object}
40 */
41const colorMap = {
42 error: 'red',
43 warn: 'yellow',
44 info: 'green',
45 verbose: 'cyan',
46 debug: 'blue',
47 silly: 'magenta'
48};
49
50const timestamp = () => (new Date()).toLocaleTimeString();
51const colorize = level => colors[colorMap[level]](level.toUpperCase());
52
53/**
54* Helper function to test if a string is a stringified version of a JSON object
55* @param {string} str - the input string to test
56* @returns {boolean} - true iff the string can be parsed as JSON
57* @private
58*/
59const isJson = (str) => {
60 try {
61 return (JSON.parse(str) && !!str);
62 } catch (e) {
63 return false;
64 }
65};
66
67/**
68* Helper function to color and format JSON objects
69* @param {any} obj - the input obj to prettify
70* @returns {any} - the prettified object
71* @private
72*/
73const prettifyJson = (obj) => {
74 if(typeof obj === 'object' || isJson(obj)) {
75 return jsonColorize(obj, { pretty: true, colors });
76 }
77 return obj;
78};
79
80/**
81* The default transport for logging at multiple levels to the console
82* @param {string} level - the required log level. e.g. error, warn, info, debug, etc.
83* @param {any} obj - the input obj to prettify
84* @returns {void} -
85* @private
86*/
87const defaultTransportShim = (level, ...args) => {
88 let mutatedLevel = level;
89 let data = args;
90 let first = data.shift();
91
92 // Flatten log object
93 if(first && typeof first === 'object' && first.level && first.message){
94 const padding = first.padding && first.padding[first.level];
95 if (first.level === 'error' && first.stack) {
96 mutatedLevel = 'error';
97 first = `${first.message}\n${first.stack}`;
98 } else if (Object.keys(levels).includes(first.level)) {
99 mutatedLevel = first.level;
100 first = first.message;
101 }
102 first = padding ? `${padding} ${first}` : first;
103 }
104
105 data.unshift(first);
106
107 const stream = ['error', 'warn'].includes(mutatedLevel) ? console.error : console.log;
108
109 stream(
110 `${timestamp()} - ${colorize(mutatedLevel)}:`,
111 ...data
112 .map(obj => obj instanceof Error ? `${obj.message}\n${obj.stack}`: obj)
113 .map(prettifyJson)
114 );
115};
116const defaultTransport = {};
117Object.keys(levels).forEach(level => {
118 defaultTransport[level] = (...args) => defaultTransportShim(level, ...args);
119});
120
121/**
122 * A utility class with static function that print to the console
123 * @private
124 */
125class Logger {
126 /**
127 * A reusable function for logging at multiple levels
128 * @param {string} level - the required log level. e.g. error, warn, info, debug, etc.
129 * @param {any} obj - the input obj to prettify
130 * @returns {void} -
131 * @private
132 */
133 static dispatch(level, ...args) {
134 if (levels[level] > levels[this.level]){
135 return;
136 }
137
138 this.transports.forEach(t => {
139 if(t[level]){
140 t[level](...args);
141 }
142 });
143 }
144
145 /**
146 * Add a custom transport for logging
147 * @param {Object} transport - The transport object should have function for the usual logging operations e.g. error, warn, info, debug, etc.
148 * @returns {void} -
149 * @private
150 */
151 static add(transport) {
152 this.transports.push(transport);
153 }
154
155 /**
156 * Write an error statement to the console.
157 *
158 * Prints to `stderr` with newline.
159 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
160 * @param {any} args -
161 * @returns {void} -
162 * @private
163 */
164 static error(...args){ return this.dispatch('error', ...args); }
165
166 /**
167 * Write a warning statement to the console.
168 *
169 * Prints to `stderr` with newline.
170 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
171 * @param {any} args -
172 * @returns {void} -
173 * @private
174 */
175 static warn(...args){ return this.dispatch('warn', ...args); }
176
177 /**
178 * Write an info statement to the console.
179 *
180 * Prints to `stdout` with newline.
181 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
182 * @param {any} args -
183 * @returns {void} -
184 * @private
185 */
186 static info(...args){ return this.dispatch('info', ...args); }
187
188 /**
189 * Write an info statement to the console. Alias for `logger.log`
190 *
191 * Prints to `stdout` with newline.
192 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
193 * @param {any} args -
194 * @returns {void} -
195 * @private
196 */
197 static log(...args){ return this.info(...args); }
198
199 /**
200 * Write an http statement to the console.
201 *
202 * Prints to `stdout` with newline.
203 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
204 * @param {any} args -
205 * @returns {void} -
206 * @private
207 */
208 static http(...args){ return this.dispatch('http', ...args); }
209
210 /**
211 * Write a verbose log statement to the console.
212 *
213 * Prints to `stdout` with newline.
214 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
215 * @param {any} args -
216 * @returns {void} -
217 * @private
218 */
219 static verbose(...args){ return this.dispatch('verbose', ...args); }
220
221 /**
222 * Write a debug statement to the console.
223 *
224 * Prints to `stdout` with newline.
225 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
226 * @param {any} args -
227 * @returns {void} -
228 * @private
229 */
230 static debug(...args){ return this.dispatch('debug', ...args); }
231
232 /**
233 * Write a silly level statement to the console.
234 *
235 * Prints to `stdout` with newline.
236 * @param {any|object} data - if this is an object with properties `level` and `message` it will be flattened first
237 * @param {any} args -
238 * @returns {void} -
239 * @private
240 */
241 static silly(...args){ return this.dispatch('silly', ...args); }
242}
243
244// Set the default logging level
245Logger.level = 'info';
246
247// A list of user-provided logging tranports
248Logger.transports = [ defaultTransport ];
249
250module.exports = Logger;