From 7b16428803940f89a4e2dbe37c6b01cb955fec2d Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 3 Feb 2015 17:43:33 -0800 Subject: [PATCH 1/3] [Logging] Add spec for log levels Add spec for log levels, to allow these to be passed at run-time in order to suppress logging during normal use. WTD-793. --- platform/framework/src/LogLevel.js | 14 +++++ platform/framework/test/LogLevelSpec.js | 84 +++++++++++++++++++++++++ platform/framework/test/suite.json | 1 + 3 files changed, 99 insertions(+) create mode 100644 platform/framework/src/LogLevel.js create mode 100644 platform/framework/test/LogLevelSpec.js diff --git a/platform/framework/src/LogLevel.js b/platform/framework/src/LogLevel.js new file mode 100644 index 0000000000..bcc4342e4d --- /dev/null +++ b/platform/framework/src/LogLevel.js @@ -0,0 +1,14 @@ +/*global define*/ + +define( + [], + function () { + "use strict"; + + function LogLevel() { + + } + + return LogLevel; + } +); \ No newline at end of file diff --git a/platform/framework/test/LogLevelSpec.js b/platform/framework/test/LogLevelSpec.js new file mode 100644 index 0000000000..c5593056f8 --- /dev/null +++ b/platform/framework/test/LogLevelSpec.js @@ -0,0 +1,84 @@ +/*global define,Promise,describe,it,expect,beforeEach,waitsFor,jasmine*/ + +define( + ['../src/LogLevel'], + function (LogLevel) { + "use strict"; + + var LOG_METHODS = [ + 'error', + 'warn', + 'info', + 'log', + 'debug' + ]; + + describe("The logging level handler", function () { + var mockLog, + mockApp, + mockProvide, + mockDelegate, + mockMethods; + + function logAll(v) { + LOG_METHODS.forEach(function (m) { + mockLog[m](v); + mockDelegate[m](v); + }); + } + + function expectCalls(calls, v) { + LOG_METHODS.forEach(function (m) { + if (calls.indexOf(m) > -1) { + expect(mockMethods[m]).toHaveBeenCalledWith(v); + } else { + expect(mockMethods[m]).not.toHaveBeenCalledWith(v); + } + }); + } + + beforeEach(function () { + mockMethods = jasmine.createSpyObj("levels", LOG_METHODS); + mockLog = jasmine.createSpyObj('$log', LOG_METHODS); + mockApp = jasmine.createSpyObj('app', ['config']); + mockProvide = jasmine.createSpyObj('$provide', ['decorator']); + mockDelegate = jasmine.createSpyObj('$delegate', LOG_METHODS); + + LOG_METHODS.forEach(function (m) { + mockLog[m].andCallFake(mockMethods[m]); + mockDelegate[m].andCallFake(mockMethods[m]); + }); + + mockApp.config.andCallFake(function (callback) { + callback(mockProvide); + }); + + mockProvide.decorator.andCallFake(function (key, callback) { + // Only $log should be configured in any case + expect(key).toEqual('$log'); + callback(mockDelegate); + }); + }); + + it("defaults to 'warn' level", function () { + new LogLevel("garbage").configure(mockApp, mockLog); + logAll("test"); + expectCalls(['error', 'warn'], 'test'); + }); + + LOG_METHODS.forEach(function (m, i) { + it("supports log level '" + m + "'", function () { + // Note: This is sensitive to ordering of LOG_METHODS, + // which needs to be highest-level-first above. + var expected = LOG_METHODS.slice(0, i + 1), + message = "test " + m; + + new LogLevel(m).configure(mockApp, mockLog); + logAll(message); + expectCalls(expected, message); + }); + }); + + }); + } +); \ No newline at end of file diff --git a/platform/framework/test/suite.json b/platform/framework/test/suite.json index 93504f01af..eda75ec012 100644 --- a/platform/framework/test/suite.json +++ b/platform/framework/test/suite.json @@ -1,5 +1,6 @@ [ "FrameworkInitializer", + "LogLevel", "bootstrap/ApplicationBootstrapper", "load/Bundle", "load/BundleLoader", From 6dd8a4cccac91603042b025e9500e74c1902b124 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 3 Feb 2015 18:01:29 -0800 Subject: [PATCH 2/3] [Logging] Implement log levels Implement suppression of logging below specific levels, for WTD-793. --- platform/framework/src/LogLevel.js | 66 +++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/platform/framework/src/LogLevel.js b/platform/framework/src/LogLevel.js index bcc4342e4d..bbcd6bcb1d 100644 --- a/platform/framework/src/LogLevel.js +++ b/platform/framework/src/LogLevel.js @@ -5,8 +5,72 @@ define( function () { "use strict"; - function LogLevel() { + // Log levels; note that these must be in order of + // most-important-first for LogLevel to function correctly + // as implemented. + var LOG_LEVELS = [ + 'error', + 'warn', + 'info', + 'log', + 'debug' + ]; + // No-op, to replace undesired log levels with + function NOOP() {} + + /** + * Handles enforcement of logging at different levels, specified + * at load time. The provided level should be one of "error", + * "warn", "info", "log", or "debug"; otherwise, "warn" is used + * as a default. Only log messages of levels equal to or greater + * than the specified level will be passed to console. + * + * @constructor + * @param {string} level the logging level + */ + function LogLevel(level) { + // Find the numeric level associated with the string + var index = LOG_LEVELS.indexOf(level); + + // Replace logging methods with no-ops, if they are + // not of an appropriate level. + function decorate(log) { + LOG_LEVELS.forEach(function (m, i) { + // Determine applicability based on index + // (since levels are in descending order) + if (i > index) { + log[m] = NOOP; + } + }); + return log; + } + + // Default to 'warn' level if unspecified + if (index < 0) { + index = 1; + } + + return { + /** + * Configure logging to suppress log output if it is + * not of an appropriate level. Both the Angular app + * being initialized and a reference to `$log` should be + * passed; the former is used to configure application + * logging, while the latter is needed to apply the + * same configuration during framework initialization + * (since the framework also logs.) + * + * @param app the Angular app to configure + * @param $log Angular's $log (also configured) + */ + configure: function (app, $log) { + decorate($log); + app.config(function ($provide) { + $provide.decorator('$log', decorate); + }); + } + }; } return LogLevel; From c0163d2d608903b8106543061741b9119754cbb4 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Tue, 3 Feb 2015 18:42:30 -0800 Subject: [PATCH 3/3] [Logging] Wire in log levels Wire in log levels to allow specifying as a query string parameter; defaults to logging only warnings and above, for WTD-793. --- platform/framework/src/LogLevel.js | 6 ++++-- platform/framework/src/Main.js | 13 +++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/platform/framework/src/LogLevel.js b/platform/framework/src/LogLevel.js index bbcd6bcb1d..d834ba83f7 100644 --- a/platform/framework/src/LogLevel.js +++ b/platform/framework/src/LogLevel.js @@ -43,7 +43,6 @@ define( log[m] = NOOP; } }); - return log; } // Default to 'warn' level if unspecified @@ -67,7 +66,10 @@ define( configure: function (app, $log) { decorate($log); app.config(function ($provide) { - $provide.decorator('$log', decorate); + $provide.decorator('$log', function ($delegate) { + decorate($delegate); + return $delegate; + }); }); } }; diff --git a/platform/framework/src/Main.js b/platform/framework/src/Main.js index d0c7978379..27bad9fb68 100644 --- a/platform/framework/src/Main.js +++ b/platform/framework/src/Main.js @@ -19,6 +19,7 @@ define( '../lib/angular-route.min', './Constants', './FrameworkInitializer', + './LogLevel', './load/BundleLoader', './resolve/ImplementationLoader', './resolve/ExtensionResolver', @@ -36,6 +37,7 @@ define( angularRoute, Constants, FrameworkInitializer, + LogLevel, BundleLoader, ImplementationLoader, ExtensionResolver, @@ -52,6 +54,12 @@ define( // services, which are useful to the framework layer. var injector = angular.injector(['ng']); + // Look up log level from query string + function logLevel() { + var match = /[?&]log=([a-z]+)/.exec(window.location.search); + return match ? match[1] : ""; + } + // Polyfill Promise, in case browser does not natively provide Promise window.Promise = window.Promise || es6promise.Promise; @@ -86,6 +94,11 @@ define( bootstrapper ); + // Apply logging levels; this must be done now, before the + // first log statement. + new LogLevel(logLevel()).configure(app, $log); + + // Initialize the application $log.info("Initializing application."); initializer.runApplication(Constants.BUNDLE_LISTING_FILE); }