UNPKG

7.29 kBJavaScriptView Raw
1/*
2 Licensed to the Apache Software Foundation (ASF) under one
3 or more contributor license agreements. See the NOTICE file
4 distributed with this work for additional information
5 regarding copyright ownership. The ASF licenses this file
6 to you under the Apache License, Version 2.0 (the
7 "License"); you may not use this file except in compliance
8 with the License. You may obtain a copy of the License at
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 Unless required by applicable law or agreed to in writing,
13 software distributed under the License is distributed on an
14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 KIND, either express or implied. See the License for the
16 specific language governing permissions and limitations
17 under the License.
18 */
19
20var ansi = require('ansi');
21var EventEmitter = require('events').EventEmitter;
22var CordovaError = require('./CordovaError/CordovaError');
23var EOL = require('os').EOL;
24
25var INSTANCE;
26
27/**
28 * @class CordovaLogger
29 *
30 * Implements logging facility that anybody could use. Should not be
31 * instantiated directly, `CordovaLogger.get()` method should be used instead
32 * to acquire logger instance
33 */
34function CordovaLogger () {
35 this.levels = {};
36 this.colors = {};
37 this.stdout = process.stdout;
38 this.stderr = process.stderr;
39
40 this.stdoutCursor = ansi(this.stdout);
41 this.stderrCursor = ansi(this.stderr);
42
43 this.addLevel('verbose', 1000, 'grey');
44 this.addLevel('normal', 2000);
45 this.addLevel('warn', 2000, 'yellow');
46 this.addLevel('info', 3000, 'blue');
47 this.addLevel('error', 5000, 'red');
48 this.addLevel('results', 10000);
49
50 this.setLevel('normal');
51}
52
53/**
54 * Static method to create new or acquire existing instance.
55 *
56 * @return {CordovaLogger} Logger instance
57 */
58CordovaLogger.get = function () {
59 return INSTANCE || (INSTANCE = new CordovaLogger());
60};
61
62CordovaLogger.VERBOSE = 'verbose';
63CordovaLogger.NORMAL = 'normal';
64CordovaLogger.WARN = 'warn';
65CordovaLogger.INFO = 'info';
66CordovaLogger.ERROR = 'error';
67CordovaLogger.RESULTS = 'results';
68
69/**
70 * Emits log message to process' stdout/stderr depending on message's severity
71 * and current log level. If severity is less than current logger's level,
72 * then the message is ignored.
73 *
74 * @param {String} logLevel The message's log level. The logger should have
75 * corresponding level added (via logger.addLevel), otherwise
76 * `CordovaLogger.NORMAL` level will be used.
77 * @param {String} message The message, that should be logged to process'
78 * stdio
79 *
80 * @return {CordovaLogger} Current instance, to allow calls chaining.
81 */
82CordovaLogger.prototype.log = function (logLevel, message) {
83 // if there is no such logLevel defined, or provided level has
84 // less severity than active level, then just ignore this call and return
85 if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel]) {
86 // return instance to allow to chain calls
87 return this;
88 }
89
90 var isVerbose = this.logLevel === 'verbose';
91 var cursor = this.stdoutCursor;
92
93 if (message instanceof Error || logLevel === CordovaLogger.ERROR) {
94 message = formatError(message, isVerbose);
95 cursor = this.stderrCursor;
96 }
97
98 var color = this.colors[logLevel];
99 if (color) {
100 cursor.bold().fg[color]();
101 }
102
103 cursor.write(message).reset().write(EOL);
104
105 return this;
106};
107
108/**
109 * Adds a new level to logger instance. This method also creates a shortcut
110 * method to log events with the level provided (i.e. after adding new level
111 * 'debug', the method `debug(message)`, equal to logger.log('debug', message),
112 * will be added to logger instance)
113 *
114 * @param {String} level A log level name. The levels with the following
115 * names added by default to every instance: 'verbose', 'normal', 'warn',
116 * 'info', 'error', 'results'
117 * @param {Number} severity A number that represents level's severity.
118 * @param {String} color A valid color name, that will be used to log
119 * messages with this level. Any CSS color code or RGB value is allowed
120 * (according to ansi documentation:
121 * https://github.com/TooTallNate/ansi.js#features)
122 *
123 * @return {CordovaLogger} Current instance, to allow calls chaining.
124 */
125CordovaLogger.prototype.addLevel = function (level, severity, color) {
126
127 this.levels[level] = severity;
128
129 if (color) {
130 this.colors[level] = color;
131 }
132
133 // Define own method with corresponding name
134 if (!this[level]) {
135 this[level] = this.log.bind(this, level);
136 }
137
138 return this;
139};
140
141/**
142 * Sets the current logger level to provided value. If logger doesn't have level
143 * with this name, `CordovaLogger.NORMAL` will be used.
144 *
145 * @param {String} logLevel Level name. The level with this name should be
146 * added to logger before.
147 *
148 * @return {CordovaLogger} Current instance, to allow calls chaining.
149 */
150CordovaLogger.prototype.setLevel = function (logLevel) {
151 this.logLevel = this.levels[logLevel] ? logLevel : CordovaLogger.NORMAL;
152
153 return this;
154};
155
156/**
157 * Adjusts the current logger level according to the passed options.
158 *
159 * @param {Object|Array} opts An object or args array with options
160 *
161 * @return {CordovaLogger} Current instance, to allow calls chaining.
162 */
163CordovaLogger.prototype.adjustLevel = function (opts) {
164 if (opts.verbose || (Array.isArray(opts) && opts.indexOf('--verbose') !== -1)) {
165 this.setLevel('verbose');
166 } else if (opts.silent || (Array.isArray(opts) && opts.indexOf('--silent') !== -1)) {
167 this.setLevel('error');
168 }
169
170 return this;
171};
172
173/**
174 * Attaches logger to EventEmitter instance provided.
175 *
176 * @param {EventEmitter} eventEmitter An EventEmitter instance to attach
177 * logger to.
178 *
179 * @return {CordovaLogger} Current instance, to allow calls chaining.
180 */
181CordovaLogger.prototype.subscribe = function (eventEmitter) {
182
183 if (!(eventEmitter instanceof EventEmitter)) { throw new Error('Subscribe method only accepts an EventEmitter instance as argument'); }
184
185 eventEmitter.on('verbose', this.verbose)
186 .on('log', this.normal)
187 .on('info', this.info)
188 .on('warn', this.warn)
189 .on('warning', this.warn)
190 // Set up event handlers for logging and results emitted as events.
191 .on('results', this.results);
192
193 return this;
194};
195
196function formatError (error, isVerbose) {
197 var message = '';
198
199 if (error instanceof CordovaError) {
200 message = error.toString(isVerbose);
201 } else if (error instanceof Error) {
202 if (isVerbose) {
203 message = error.stack;
204 } else {
205 message = error.message;
206 }
207 } else {
208 // Plain text error message
209 message = error;
210 }
211
212 if (typeof message === 'string' && message.toUpperCase().indexOf('ERROR:') !== 0) {
213 // Needed for backward compatibility with external tools
214 message = 'Error: ' + message;
215 }
216
217 return message;
218}
219
220module.exports = CordovaLogger;