cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From an-s...@apache.org
Subject cordova-lib git commit: CB-10176 Adds CordovaLogger class, based on logger module from cordova-cli
Date Thu, 04 Feb 2016 07:11:34 GMT
Repository: cordova-lib
Updated Branches:
  refs/heads/master b1bdd5569 -> 1fd2dd857


CB-10176 Adds CordovaLogger class, based on logger module from cordova-cli


Project: http://git-wip-us.apache.org/repos/asf/cordova-lib/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-lib/commit/1fd2dd85
Tree: http://git-wip-us.apache.org/repos/asf/cordova-lib/tree/1fd2dd85
Diff: http://git-wip-us.apache.org/repos/asf/cordova-lib/diff/1fd2dd85

Branch: refs/heads/master
Commit: 1fd2dd857dca026483d869660e79784f392921d0
Parents: b1bdd55
Author: Vladimir Kotikov <v-vlkoti@microsoft.com>
Authored: Wed Feb 3 17:47:24 2016 +0300
Committer: Vladimir Kotikov <v-vlkoti@microsoft.com>
Committed: Wed Feb 3 17:48:06 2016 +0300

----------------------------------------------------------------------
 cordova-common/cordova-common.js          |   1 +
 cordova-common/package.json               |  87 +++++------
 cordova-common/spec/CordovaLogger.spec.js | 164 ++++++++++++++++++++
 cordova-common/src/CordovaLogger.js       | 203 +++++++++++++++++++++++++
 4 files changed, 412 insertions(+), 43 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/1fd2dd85/cordova-common/cordova-common.js
----------------------------------------------------------------------
diff --git a/cordova-common/cordova-common.js b/cordova-common/cordova-common.js
index 59b52fc..22e90a7 100644
--- a/cordova-common/cordova-common.js
+++ b/cordova-common/cordova-common.js
@@ -26,6 +26,7 @@ exports = module.exports = {
 
     ActionStack: require('./src/ActionStack'),
     CordovaError: require('./src/CordovaError/CordovaError'),
+    CordovaLogger: require('./src/CordovaLogger'),
     CordovaExternalToolErrorContext: require('./src/CordovaError/CordovaExternalToolErrorContext'),
     PlatformJson: require('./src/PlatformJson'),
     ConfigParser: require('./src/ConfigParser/ConfigParser.js'),

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/1fd2dd85/cordova-common/package.json
----------------------------------------------------------------------
diff --git a/cordova-common/package.json b/cordova-common/package.json
index 40ed07c..a9d9fc0 100644
--- a/cordova-common/package.json
+++ b/cordova-common/package.json
@@ -1,45 +1,46 @@
 {
-    "author": "Apache Software Foundation",
-    "name": "cordova-common",
-    "description": "Apache Cordova tools and platforms shared routines",
-    "license": "Apache-2.0",
-    "version": "1.1.0-dev",
-    "repository": {
-        "type": "git",
-        "url": "git://git-wip-us.apache.org/repos/asf/cordova-common.git"
-    },
-    "bugs": {
-        "url": "https://issues.apache.org/jira/browse/CB",
-        "email": "dev@cordova.apache.org"
-    },
-    "main": "cordova-common.js",
-    "engines": {
-        "node": ">=0.9.9"
-    },
-    "scripts": {
-        "test": "npm run jshint && npm run jasmine",
-        "jshint": "node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint
spec",
-        "jasmine": "node node_modules/jasmine-node/bin/jasmine-node --captureExceptions --color
spec",
-        "cover": "node node_modules/istanbul/lib/cli.js cover --root src --print detail node_modules/jasmine-node/bin/jasmine-node
-- spec"
-    },
-    "engineStrict": true,
-    "dependencies": {
-        "bplist-parser": "^0.1.0",
-        "cordova-registry-mapper": "^1.1.8",
-        "elementtree": "^0.1.6",
-        "glob": "^5.0.13",
-        "osenv": "^0.1.3",
-        "plist": "^1.2.0",
-        "q": "^1.4.1",
-        "semver": "^5.0.1",
-        "shelljs": "^0.5.1",
-        "underscore": "^1.8.3",
-        "unorm": "^1.3.3"
-    },
-    "devDependencies": {
-        "istanbul": "^0.3.17",
-        "jasmine-node": "^1.14.5",
-        "jshint": "^2.8.0"
-    },
-    "contributors": []
+  "author": "Apache Software Foundation",
+  "name": "cordova-common",
+  "description": "Apache Cordova tools and platforms shared routines",
+  "license": "Apache-2.0",
+  "version": "1.1.0-dev",
+  "repository": {
+    "type": "git",
+    "url": "git://git-wip-us.apache.org/repos/asf/cordova-common.git"
+  },
+  "bugs": {
+    "url": "https://issues.apache.org/jira/browse/CB",
+    "email": "dev@cordova.apache.org"
+  },
+  "main": "cordova-common.js",
+  "engines": {
+    "node": ">=0.9.9"
+  },
+  "scripts": {
+    "test": "npm run jshint && npm run jasmine",
+    "jshint": "node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint
spec",
+    "jasmine": "node node_modules/jasmine-node/bin/jasmine-node --captureExceptions --color
spec",
+    "cover": "node node_modules/istanbul/lib/cli.js cover --root src --print detail node_modules/jasmine-node/bin/jasmine-node
-- spec"
+  },
+  "engineStrict": true,
+  "dependencies": {
+    "ansi": "^0.3.1",
+    "bplist-parser": "^0.1.0",
+    "cordova-registry-mapper": "^1.1.8",
+    "elementtree": "^0.1.6",
+    "glob": "^5.0.13",
+    "osenv": "^0.1.3",
+    "plist": "^1.2.0",
+    "q": "^1.4.1",
+    "semver": "^5.0.1",
+    "shelljs": "^0.5.1",
+    "underscore": "^1.8.3",
+    "unorm": "^1.3.3"
+  },
+  "devDependencies": {
+    "istanbul": "^0.3.17",
+    "jasmine-node": "^1.14.5",
+    "jshint": "^2.8.0"
+  },
+  "contributors": []
 }

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/1fd2dd85/cordova-common/spec/CordovaLogger.spec.js
----------------------------------------------------------------------
diff --git a/cordova-common/spec/CordovaLogger.spec.js b/cordova-common/spec/CordovaLogger.spec.js
new file mode 100644
index 0000000..fd96488
--- /dev/null
+++ b/cordova-common/spec/CordovaLogger.spec.js
@@ -0,0 +1,164 @@
+/**
+    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 CordovaError = require('../src/CordovaError/CordovaError');
+var CordovaLogger = require('../src/CordovaLogger');
+var EventEmitter = require('events').EventEmitter;
+
+var DEFAULT_LEVELS = ['verbose', 'normal', 'warn', 'info', 'error', 'results'];
+
+describe('CordovaLogger class', function() {
+    it('should be constructable', function () {
+        expect(new CordovaLogger()).toEqual(jasmine.any(CordovaLogger));
+    });
+
+    it('should expose default levels as constants', function () {
+        DEFAULT_LEVELS.forEach(function (level) {
+            var constant = level.toUpperCase();
+            expect(CordovaLogger[constant]).toBeDefined();
+            expect(CordovaLogger[constant]).toBe(level);
+        });
+    });
+
+    it('should return the same instance via "get" method', function () {
+        expect(CordovaLogger.get()).toBeDefined();
+        expect(CordovaLogger.get()).toBe(CordovaLogger.get());
+        expect(CordovaLogger.get()).toEqual(jasmine.any(CordovaLogger));
+    });
+
+    describe('instance', function () {
+
+        var logger;
+
+        beforeEach(function () {
+            logger = new CordovaLogger();
+        });
+
+        it('should have defaults levels', function () {
+            DEFAULT_LEVELS.forEach(function (level) {
+                expect(logger.levels[level]).toBeDefined();
+                expect(logger.levels[level]).toEqual(jasmine.any(Number));
+                expect(logger[level]).toBeDefined();
+                expect(logger[level]).toEqual(jasmine.any(Function));
+                expect(logger[level].length).toBe(1);
+            });
+        });
+
+        describe('addLevel method', function () {
+            it('should add a new level and a corresponding shortcut method', function ()
{
+                spyOn(logger, 'log');
+                logger.addLevel('debug', 100000, 'grey');
+                expect(logger.levels.debug).toBe(100000);
+                expect(logger.debug).toEqual(jasmine.any(Function));
+
+                logger.debug('debug message');
+                expect(logger.log).toHaveBeenCalledWith('debug', 'debug message');
+            });
+
+            it('should not add a shortcut method fi the property with the same name already
exists', function () {
+                var logMethod = logger.log;
+                logger.addLevel('log', 500);
+                expect(logger.log).toBe(logMethod); // "log" method remains unchanged
+            });
+        });
+
+        describe('setLevel method', function () {
+            it('should set logger\'s level to \'NORMAL\' if provided level does not exist',
function () {
+                logger.setLevel('debug');
+                expect(logger.logLevel).toBe(CordovaLogger.NORMAL); // default value
+            });
+        });
+
+        describe('subscribe method', function () {
+            it('should throw if called without EventEmitter instance', function () {
+                expect(function () { logger.subscribe(); }).toThrow();
+                expect(function () { logger.subscribe(123); }).toThrow();
+            });
+
+            it('should attach corresponding listeners to supplied emitter', function () {
+
+                var eventNamesExclusions = {
+                    log: 'normal',
+                    warning: 'warn'
+                };
+
+                var listenerSpy = jasmine.createSpy('listenerSpy')
+                .andCallFake(function (eventName) {
+                    eventName = eventNamesExclusions[eventName] || eventName;
+                    expect(logger.levels[eventName]).toBeDefined();
+                });
+
+                var emitter = new EventEmitter().on('newListener', listenerSpy);
+                logger.subscribe(emitter);
+            });
+        });
+
+        describe('log method', function () {
+
+            function CursorSpy (name) {
+                var cursorMethods = ['reset', 'write'];
+                var spy = jasmine.createSpyObj(name, cursorMethods);
+
+                // Make spy methods chainable, as original Cursor acts
+                cursorMethods.forEach(function (method) { spy[method].andReturn(spy); });
+
+                return spy;
+            }
+
+            beforeEach(function () {
+                // Empty colors table to make it easier to mock
+                logger.colors = {};
+                logger.stdoutCursor = new CursorSpy('stdoutCursor');
+                logger.stderrCursor = new CursorSpy('stderrCursor');
+            });
+
+            it('should ignore message if severity is less than logger\'s level', function
() {
+                logger.setLevel('error').log('verbose', 'some_messgge');
+                expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
+                expect(logger.stderrCursor.write).not.toHaveBeenCalled();
+            });
+
+            it('should log everything except error messages to stdout', function () {
+                logger.setLevel('verbose');
+                DEFAULT_LEVELS.forEach(function (level) {
+                    logger.log(level, 'message');
+                });
+
+                // Multiply calls number to 2 because 'write' method is get called twice
(with message and EOL)
+                expect(logger.stdoutCursor.write.calls.length).toBe((DEFAULT_LEVELS.length
- 1) * 2);
+                expect(logger.stderrCursor.write.calls.length).toBe(1 * 2);
+            });
+
+            it('should log Error objects to stderr despite of loglevel', function () {
+                logger.setLevel('verbose').log('verbose', new Error());
+                expect(logger.stdoutCursor.write).not.toHaveBeenCalled();
+                expect(logger.stderrCursor.write).toHaveBeenCalled();
+            });
+
+            it('should handle CordovaError instances separately from Error ones', function
() {
+                var errorMock = new CordovaError();
+                spyOn(errorMock, 'toString').andReturn('error_message');
+
+                logger.setLevel('verbose').log('verbose', errorMock);
+                expect(errorMock.toString).toHaveBeenCalled();
+                expect(logger.stderrCursor.write.calls[0].args[0]).toBe('Error: error_message');
+            });
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-lib/blob/1fd2dd85/cordova-common/src/CordovaLogger.js
----------------------------------------------------------------------
diff --git a/cordova-common/src/CordovaLogger.js b/cordova-common/src/CordovaLogger.js
new file mode 100644
index 0000000..38bfe69
--- /dev/null
+++ b/cordova-common/src/CordovaLogger.js
@@ -0,0 +1,203 @@
+/*
+ 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;
+};
+
+/**
+ * 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(message.toUpperCase().indexOf('ERROR:') !== 0) {
+        // Needed for backward compatibility with external tools
+        message = 'Error: ' + message;
+    }
+
+    return message;
+}
+
+module.exports = CordovaLogger;


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


Mime
View raw message