| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| var ansi = require('ansi'); |
| var EventEmitter = require('events').EventEmitter; |
| var CordovaError = require('./CordovaError/CordovaError'); |
| var EOL = require('os').EOL; |
| |
| var INSTANCE; |
| |
| /** |
| * @class CordovaLogger |
| * |
| * Implements logging facility that anybody could use. Should not be |
| * instantiated directly, `CordovaLogger.get()` method should be used instead |
| * to acquire logger instance |
| */ |
| function CordovaLogger () { |
| this.levels = {}; |
| this.colors = {}; |
| this.stdout = process.stdout; |
| this.stderr = process.stderr; |
| |
| this.stdoutCursor = ansi(this.stdout); |
| this.stderrCursor = ansi(this.stderr); |
| |
| this.addLevel('verbose', 1000, 'grey'); |
| this.addLevel('normal', 2000); |
| this.addLevel('warn', 2000, 'yellow'); |
| this.addLevel('info', 3000, 'blue'); |
| this.addLevel('error', 5000, 'red'); |
| this.addLevel('results', 10000); |
| |
| this.setLevel('normal'); |
| } |
| |
| /** |
| * Static method to create new or acquire existing instance. |
| * |
| * @return {CordovaLogger} Logger instance |
| */ |
| CordovaLogger.get = function () { |
| return INSTANCE || (INSTANCE = new CordovaLogger()); |
| }; |
| |
| CordovaLogger.VERBOSE = 'verbose'; |
| CordovaLogger.NORMAL = 'normal'; |
| CordovaLogger.WARN = 'warn'; |
| CordovaLogger.INFO = 'info'; |
| CordovaLogger.ERROR = 'error'; |
| CordovaLogger.RESULTS = 'results'; |
| |
| /** |
| * Emits log message to process' stdout/stderr depending on message's severity |
| * and current log level. If severity is less than current logger's level, |
| * then the message is ignored. |
| * |
| * @param {String} logLevel The message's log level. The logger should have |
| * corresponding level added (via logger.addLevel), otherwise |
| * `CordovaLogger.NORMAL` level will be used. |
| * @param {String} message The message, that should be logged to process' |
| * stdio |
| * |
| * @return {CordovaLogger} Current instance, to allow calls chaining. |
| */ |
| CordovaLogger.prototype.log = function (logLevel, message) { |
| // if there is no such logLevel defined, or provided level has |
| // less severity than active level, then just ignore this call and return |
| if (!this.levels[logLevel] || this.levels[logLevel] < this.levels[this.logLevel]) { |
| // return instance to allow to chain calls |
| return this; |
| } |
| |
| var isVerbose = this.logLevel === 'verbose'; |
| var cursor = this.stdoutCursor; |
| |
| if (message instanceof Error || logLevel === CordovaLogger.ERROR) { |
| message = formatError(message, isVerbose); |
| cursor = this.stderrCursor; |
| } |
| |
| var color = this.colors[logLevel]; |
| if (color) { |
| cursor.bold().fg[color](); |
| } |
| |
| cursor.write(message).reset().write(EOL); |
| |
| return this; |
| }; |
| |
| /** |
| * Adds a new level to logger instance. This method also creates a shortcut |
| * method to log events with the level provided (i.e. after adding new level |
| * 'debug', the method `debug(message)`, equal to logger.log('debug', message), |
| * will be added to logger instance) |
| * |
| * @param {String} level A log level name. The levels with the following |
| * names added by default to every instance: 'verbose', 'normal', 'warn', |
| * 'info', 'error', 'results' |
| * @param {Number} severity A number that represents level's severity. |
| * @param {String} color A valid color name, that will be used to log |
| * messages with this level. Any CSS color code or RGB value is allowed |
| * (according to ansi documentation: |
| * https://github.com/TooTallNate/ansi.js#features) |
| * |
| * @return {CordovaLogger} Current instance, to allow calls chaining. |
| */ |
| CordovaLogger.prototype.addLevel = function (level, severity, color) { |
| |
| this.levels[level] = severity; |
| |
| if (color) { |
| this.colors[level] = color; |
| } |
| |
| // Define own method with corresponding name |
| if (!this[level]) { |
| this[level] = this.log.bind(this, level); |
| } |
| |
| return this; |
| }; |
| |
| /** |
| * Sets the current logger level to provided value. If logger doesn't have level |
| * with this name, `CordovaLogger.NORMAL` will be used. |
| * |
| * @param {String} logLevel Level name. The level with this name should be |
| * added to logger before. |
| * |
| * @return {CordovaLogger} Current instance, to allow calls chaining. |
| */ |
| CordovaLogger.prototype.setLevel = function (logLevel) { |
| this.logLevel = this.levels[logLevel] ? logLevel : CordovaLogger.NORMAL; |
| |
| return this; |
| }; |
| |
| /** |
| * Adjusts the current logger level according to the passed options. |
| * |
| * @param {Object|Array} opts An object or args array with options |
| * |
| * @return {CordovaLogger} Current instance, to allow calls chaining. |
| */ |
| CordovaLogger.prototype.adjustLevel = function (opts) { |
| if (opts.verbose || (Array.isArray(opts) && opts.includes('--verbose'))) { |
| this.setLevel('verbose'); |
| } else if (opts.silent || (Array.isArray(opts) && opts.includes('--silent'))) { |
| this.setLevel('error'); |
| } |
| |
| return this; |
| }; |
| |
| /** |
| * Attaches logger to EventEmitter instance provided. |
| * |
| * @param {EventEmitter} eventEmitter An EventEmitter instance to attach |
| * logger to. |
| * |
| * @return {CordovaLogger} Current instance, to allow calls chaining. |
| */ |
| CordovaLogger.prototype.subscribe = function (eventEmitter) { |
| |
| if (!(eventEmitter instanceof EventEmitter)) { throw new Error('Subscribe method only accepts an EventEmitter instance as argument'); } |
| |
| eventEmitter.on('verbose', this.verbose) |
| .on('log', this.normal) |
| .on('info', this.info) |
| .on('warn', this.warn) |
| .on('warning', this.warn) |
| // Set up event handlers for logging and results emitted as events. |
| .on('results', this.results); |
| |
| return this; |
| }; |
| |
| function formatError (error, isVerbose) { |
| var message = ''; |
| |
| if (error instanceof CordovaError) { |
| message = error.toString(isVerbose); |
| } else if (error instanceof Error) { |
| if (isVerbose) { |
| message = error.stack; |
| } else { |
| message = error.message; |
| } |
| } else { |
| // Plain text error message |
| message = error; |
| } |
| |
| if (typeof message === 'string' && !message.toUpperCase().startsWith('ERROR:')) { |
| // Needed for backward compatibility with external tools |
| message = 'Error: ' + message; |
| } |
| |
| return message; |
| } |
| |
| module.exports = CordovaLogger; |