From 39da428eb15716cf526b920acfbe288e5f400e89 Mon Sep 17 00:00:00 2001 From: Jagadeesh Karicherla Date: Thu, 7 Mar 2024 23:12:04 -0800 Subject: [PATCH] feat(core): add logger with loglevel support add logger with loglevel support --- .mocharc.js | 2 +- Makefile | 1 + src/6_branch.js | 23 ++++++--- src/8_logger.js | 94 +++++++++++++++++++++++++++++++++++ test/6_branch_new.js | 28 +++++++---- test/8_logger.js | 113 +++++++++++++++++++++++++++++++++++++++++++ test/branch-deps.js | 8 +-- 7 files changed, 248 insertions(+), 21 deletions(-) create mode 100644 src/8_logger.js create mode 100644 test/8_logger.js diff --git a/.mocharc.js b/.mocharc.js index 4b5c5897..9c20d378 100644 --- a/.mocharc.js +++ b/.mocharc.js @@ -7,5 +7,5 @@ module.exports = { require: ['./node_modules/google-closure-library', './test/branch-deps.js', './node_modules/jsdom-global/register.js', './test/test-utils.js', './node_modules/sinon/lib/sinon.js'], - spec: ['./test/0_config.js','./test/0_queue.js','./test/1_utils.js','./test/6_branch_new.js', './test/journeys_utils.js'] + spec: ['./test/0_config.js','./test/0_queue.js','./test/1_utils.js','./test/6_branch_new.js', './test/journeys_utils.js', './test/8_logger.js'] }; diff --git a/Makefile b/Makefile index 66f0c8c9..24ef90ca 100644 --- a/Makefile +++ b/Makefile @@ -18,6 +18,7 @@ src/4_banner_css.js src/4_banner_html.js\ src/5_banner.js\ src/6_branch.js\ src/7_initialization.js\ +src/8_logger.js\ src/branch_view.js\ src/journeys_utils.js diff --git a/src/6_branch.js b/src/6_branch.js index 65222f4c..3360191f 100644 --- a/src/6_branch.js +++ b/src/6_branch.js @@ -16,6 +16,7 @@ goog.require('config'); goog.require('safejson'); goog.require('branch_view'); goog.require('journeys_utils'); +goog.require('Logger'); /*globals Ti, BranchStorage, require */ @@ -133,6 +134,8 @@ Branch = function() { this._server = new Server(); + this._logger = new Logger(); + var sdk = 'web'; /** @type {Array} */ @@ -373,6 +376,7 @@ Branch.prototype['init'] = wrap( utils.userPreferences.enableExtendedJourneysAssist = options && options['enableExtendedJourneysAssist'] ? options['enableExtendedJourneysAssist'] : utils.userPreferences.enableExtendedJourneysAssist; utils.extendedJourneysAssistExpiryTime = options && options['extendedJourneysAssistExpiryTime'] && Number.isInteger(options['extendedJourneysAssistExpiryTime']) ? options['extendedJourneysAssistExpiryTime'] : utils.extendedJourneysAssistExpiryTime; utils.userPreferences.allowErrorsInCallback = false; + this._logger.setLevel(options['logLevel'] || 'error'); utils.getClientHints(); if (utils.userPreferences.trackingDisabled) { @@ -1008,7 +1012,7 @@ Branch.prototype['track'] = wrap(callback_params.CALLBACK_ERR, function(done, ev } else { - console.warn("track method currently supports only pageview event."); + this._logger.log('warn', 'track method currently supports only pageview event.'); } }); @@ -1694,6 +1698,7 @@ Branch.prototype['closeJourney'] = wrap(callback_params.CALLBACK_ERR, function(d journeys_utils.animateBannerExit(journeys_utils.banner, true); } else { + this._logger.log('info', 'Journey already dismissed.'); return done('Journey already dismissed.'); } }); @@ -1702,10 +1707,10 @@ Branch.prototype['closeJourney'] = wrap(callback_params.CALLBACK_ERR, function(d Branch.prototype['banner'] = wrap(callback_params.CALLBACK_ERR, function(done, options, data) { var banner_deprecation_msg = 'The "banner" method is deprecated and will be removed in future versions. Please use Branch Journeys instead. For more information and migration steps, visit: https://help.branch.io/using-branch/docs/journeys-overview'; - console.warn(banner_deprecation_msg); + this._logger.log('warn', banner_deprecation_msg); var platform = utils.getPlatformByUserAgent(); if ([ "other", "desktop" ].includes(platform)) { - console.info("banner functionality is not supported on this platform"); + this._logger.log('info', 'banner functionality is not supported on this platform'); } else { data = data || {}; @@ -1945,6 +1950,10 @@ Branch.prototype['setAPIResponseCallback'] = wrap(callback_params.NO_CALLBACK, f * Gets the referring link from storage (session, local) wih link expiry applied if provided. */ Branch.prototype['referringLink'] = function(withExtendedJourneysAssist) { + if (!utils.isBoolean(withExtendedJourneysAssist)) { + this._logger.log('error', `referringLink: parameter withExtendedJourneysAssist must be boolean`); + return; + } return this._referringLink(withExtendedJourneysAssist); }; @@ -1959,7 +1968,7 @@ Branch.prototype['setDMAParamsForEEA'] = wrap(callback_params.CALLBACK_ERR, func try { const validateParam = (param, paramName) => { if (!utils.isBoolean(param)) { - console.warn(`setDMAParamsForEEA: ${paramName} must be boolean, but got ${param}`); + this._logger.log('error', `setDMAParamsForEEA: ${paramName} must be boolean, but got ${param}`); return false; } return true; @@ -1980,7 +1989,7 @@ Branch.prototype['setDMAParamsForEEA'] = wrap(callback_params.CALLBACK_ERR, func this._storage.set('branch_dma_data', safejson.stringify(dmaObj), true); } catch (e) { - console.error("setDMAParamsForEEA::An error occurred while setting DMA parameters for EEA", e); + this._logger.log('error', 'setDMAParamsForEEA::An error occurred while setting DMA parameters for EEA', e); } done(); }, true); @@ -2004,7 +2013,7 @@ Branch.prototype['setRequestMetaData'] = function(key, value) { this.requestMetadata = utils.addPropertyIfNotNull(this.requestMetadata, key, value); } catch (e) { - console.error("An error occured while setting request metadata", e); + this._logger.log('error', 'An error occured while setting request metadata', e); } }; @@ -2015,7 +2024,7 @@ Branch.prototype['setRequestMetaData'] = function(key, value) { */ Branch.prototype['setAPIUrl'] = function(url) { if (!utils.isValidURL(url)) { - console.error("setAPIUrl: Invalid URL format. Default URL will be set."); + this._logger.log('error', 'setAPIUrl: Invalid URL format. Default URL will be set.'); return; } diff --git a/src/8_logger.js b/src/8_logger.js new file mode 100644 index 00000000..54b1bbfa --- /dev/null +++ b/src/8_logger.js @@ -0,0 +1,94 @@ +'use strict'; +goog.provide('Logger'); +// jscs:disable validateIndentation +/** + * @constructor + */ + +Logger = function() { + /** + * @private + * @type {string} + */ + this.level_ = 'error'; + this.levelsOrdered = [ 'verbose', 'info', 'warn', 'error', 'none' ]; +}; + +/** + * Sets the logging level. + * @param {string} level - The logging level to set. + */ +Logger.prototype.setLevel = function(level) { + if (this.levelsOrdered.indexOf(level) !== -1) { + this.level_ = level; + } + else { + console.error(`Invalid log level: ${level}`); + } +}; + +/** + * Logs a message to the console if the log level allows it. + * @param {string} level - The logging level of the message. + * @param {...*} args - The message to log. + */ +Logger.prototype.log = function(level) { + var args = Array.prototype.slice.call(arguments, 1); + if (this.shouldLog(level)) { + switch (level) { + case 'info': + this.logInfo_(args); + break; + case 'warn': + this.logWarning_(args); + break; + case 'error': + this.logError_(args); + break; + } + } +}; + +/** + * Checks if a message with the given level should be logged. + * @private + * @param {string} level - The logging level of the message. + * @return {boolean} True if the message should be logged, false otherwise. + */ +Logger.prototype.shouldLog = function(level) { + if (this.level_ === 'none') { + return false; + } + let currentLevelIndex = this.levelsOrdered.indexOf(this.level_); + let logLevelIndex = this.levelsOrdered.indexOf(level); + return logLevelIndex >= currentLevelIndex; +}; + +/** + * Logs an info message to the console. + * @private + * @param {Array} args - The message to log. + */ +Logger.prototype.logInfo_ = function(args) { + console.info.apply(console, args); +}; + +/** + * Logs a warning message to the console. + * @private + * @param {Array} args - The message to log. + */ +Logger.prototype.logWarning_ = function(args) { + console.warn.apply(console, args); +}; + +/** + * Logs an error message to the console. + * @private + * @param {Array} args - The message to log. + */ +Logger.prototype.logError_ = function(args) { + console.error.apply(console, args); +}; +// jscs:enable validateIndentation + diff --git a/test/6_branch_new.js b/test/6_branch_new.js index 669f3e9d..10cf0d18 100644 --- a/test/6_branch_new.js +++ b/test/6_branch_new.js @@ -6,6 +6,7 @@ var sinon = require('sinon'); goog.require('Branch'); goog.require('utils'); goog.require('task_queue'); +goog.require('Logger'); describe('Branch - new', function() { const sandbox = sinon.createSandbox(); @@ -67,7 +68,8 @@ describe('Branch - new', function() { _storage: { set: () => {} }, - _queue: task_queue() + _queue: task_queue(), + _logger: new Logger() }; const storageSetStub = sandbox.stub(thisObj._storage, 'set'); const dmaObj = {}; @@ -83,7 +85,8 @@ describe('Branch - new', function() { _storage: { set: () => {} }, - _queue: task_queue() + _queue: task_queue(), + _logger: new Logger() }; const storageSetStub = sandbox.stub(thisObj._storage, 'set'); branch_instance.setDMAParamsForEEA.call(thisObj); @@ -94,7 +97,8 @@ describe('Branch - new', function() { _storage: { set: () => {} }, - _queue: task_queue() + _queue: task_queue(), + _logger: new Logger() }; const storageSetStub = sandbox.stub(thisObj._storage, 'set'); const dmaObj = {}; @@ -109,9 +113,10 @@ describe('Branch - new', function() { _storage: { set: () => {} }, - _queue: task_queue() + _queue: task_queue(), + _logger: new Logger() }; - const consoleErrorStub = sandbox.stub(console, 'warn'); + const consoleErrorStub = sandbox.stub(console, 'error'); try { const dmaObj = {}; dmaObj.eeaRegion = null; @@ -129,9 +134,10 @@ describe('Branch - new', function() { _storage: { set: () => {} }, - _queue: task_queue() + _queue: task_queue(), + _logger: new Logger() }; - const consoleErrorStub = sandbox.stub(console, 'warn'); + const consoleErrorStub = sandbox.stub(console, 'error'); try { const dmaObj = {}; dmaObj.eeaRegion = true; @@ -149,9 +155,10 @@ describe('Branch - new', function() { _storage: { set: () => {} }, - _queue: task_queue() + _queue: task_queue(), + _logger: new Logger() }; - const consoleErrorStub = sandbox.stub(console, 'warn'); + const consoleErrorStub = sandbox.stub(console, 'error'); try { const dmaObj = {}; dmaObj.eeaRegion = true; @@ -169,7 +176,8 @@ describe('Branch - new', function() { _storage: { set: () => {} }, - _queue: task_queue() + _queue: task_queue(), + _logger: new Logger() }; sandbox.stub(thisObj._storage, 'set').throws(new Error('Mock error')); const consoleErrorStub = sandbox.stub(console, 'error'); diff --git a/test/8_logger.js b/test/8_logger.js new file mode 100644 index 00000000..2b797e84 --- /dev/null +++ b/test/8_logger.js @@ -0,0 +1,113 @@ +'use strict'; +/*jshint -W079 */ +/*jshint esversion: 6 */ +// jscs:disable validateIndentation +var sinon = require('sinon'); +goog.require('Logger'); + +describe('Logger', function() { + const assert = testUtils.unplanned(); + const logger_instance = new Logger(); + const sandbox = sinon.createSandbox(); + afterEach(function() { + sandbox.restore(); + sinon.restore(); + }); + describe('setLevel', function() { + it('test method exists', function() { + sinon.assert.match(typeof logger_instance.setLevel, "function"); + }); + it('should set the logging level to the specified value', function() { + logger_instance.setLevel('info'); + assert.equal(logger_instance.level_, 'info'); + }); + it('should not set the logging level to an invalid value', function() { + logger_instance.setLevel('invalid'); + assert.equal(logger_instance.level_, 'info'); + }); + it('should console an error when the logging level is set to an invalid value', function() { + const consoleErrorStub = sandbox.stub(console, 'error'); + logger_instance.setLevel('misc'); + sinon.assert.calledWith(consoleErrorStub, 'Invalid log level: misc'); + }); + }); + describe('shouldLog', function() { + it('test method exists', function() { + sinon.assert.match(typeof logger_instance.shouldLog, "function"); + }); + it('should return true if the logging level allows logging the message', function() { + logger_instance.setLevel('info'); + assert.equal(logger_instance.shouldLog('info'), true); + }); + it('should return false if the logging level prevents logging the message', function() { + logger_instance.setLevel('none'); + assert.equal(logger_instance.shouldLog('info'), false); + }); + }); + describe('log', function() { + it('test method exists', function() { + sinon.assert.match(typeof logger_instance.log, "function"); + }); + it('should log an error message to the console', function() { + const consoleErrorStub = sandbox.stub(console, 'error'); + logger_instance.setLevel('error'); + logger_instance.log('error', 'This is an error message'); + sinon.assert.calledWith(consoleErrorStub, 'This is an error message'); + }); + it('should log an warn message to the console', function() { + const consoleErrorStub = sandbox.stub(console, 'warn'); + logger_instance.setLevel('warn'); + logger_instance.log('warn', 'This is a warning message'); + sinon.assert.calledWith(consoleErrorStub, 'This is a warning message'); + }); + it('should log an info message to the console', function() { + const consoleInfoStub = sandbox.stub(console, 'info'); + logger_instance.setLevel('info'); + logger_instance.log('info', 'This is a info message'); + sinon.assert.calledWith(consoleInfoStub, 'This is a info message'); + }); + it('should log an info message to the console when level is verbose', function() { + const consoleInfoStub = sandbox.stub(console, 'info'); + logger_instance.setLevel('verbose'); + logger_instance.log('info', 'This is a info message'); + sinon.assert.calledWith(consoleInfoStub, 'This is a info message'); + }); + it('should log an warn message to the console when level is verbose', function() { + const consoleWarnStub = sandbox.stub(console, 'warn'); + logger_instance.setLevel('verbose'); + logger_instance.log('warn', 'This is a warning message'); + sinon.assert.calledWith(consoleWarnStub, 'This is a warning message'); + }); + it('should log an error message to the console when level is verbose', function() { + const consoleErrorStub = sandbox.stub(console, 'error'); + logger_instance.setLevel('verbose'); + logger_instance.log('error', 'This is an error message'); + sinon.assert.calledWith(consoleErrorStub, 'This is an error message'); + }); + it('should not log an info message when level is none', function() { + const consoleInfoStub = sandbox.stub(console, 'info'); + logger_instance.setLevel('none'); + logger_instance.log('info', 'This is a info message'); + sinon.assert.notCalled(consoleInfoStub); + }); + it('should not log an warn message when level is none', function() { + const consoleWarnStub = sandbox.stub(console, 'warn'); + logger_instance.setLevel('none'); + logger_instance.log('warn', 'This is a warn message'); + sinon.assert.notCalled(consoleWarnStub); + }); + it('should not log an error message when level is none', function() { + const consoleErrorStub = sandbox.stub(console, 'error'); + logger_instance.setLevel('none'); + logger_instance.log('error', 'This is a error message'); + sinon.assert.notCalled(consoleErrorStub); + }); + it('should not log an info message when level is error', function() { + const consoleInfoStub = sandbox.stub(console, 'info'); + logger_instance.setLevel('error'); + logger_instance.log('info', 'This is a info message'); + sinon.assert.notCalled(consoleInfoStub); + }); + }); +}); +// jscs:enable validateIndentation diff --git a/test/branch-deps.js b/test/branch-deps.js index 1a7ae194..e8190154 100644 --- a/test/branch-deps.js +++ b/test/branch-deps.js @@ -5,13 +5,14 @@ goog.addDependency('../../../../src/1_utils.js', ['utils'], ['config', 'goog.jso goog.addDependency('../../../../src/2_resources.js', ['resources'], ['config', 'utils']); goog.addDependency('../../../../src/2_session.js', ['session'], ['goog.json', 'safejson', 'storage', 'utils']); goog.addDependency('../../../../src/2_storage.js', ['storage'], ['goog.json', 'utils']); -goog.addDependency('../../../../src/3_api.js', ['Server'], ['goog.json', 'safejson', 'storage', 'utils'], {'lang': 'es6'}); +goog.addDependency('../../../../src/3_api.js', ['Server'], ['goog.json', 'safejson', 'storage', 'utils']); goog.addDependency('../../../../src/3_banner_utils.js', ['banner_utils'], ['safejson', 'storage', 'utils']); goog.addDependency('../../../../src/4_banner_css.js', ['banner_css'], ['banner_utils', 'utils']); goog.addDependency('../../../../src/4_banner_html.js', ['banner_html'], ['banner_utils', 'session', 'storage', 'utils']); goog.addDependency('../../../../src/5_banner.js', ['banner'], ['banner_css', 'banner_html', 'banner_utils', 'utils']); -goog.addDependency('../../../../src/6_branch.js', ['Branch'], ['Server', 'banner', 'branch_view', 'config', 'goog.json', 'journeys_utils', 'resources', 'safejson', 'session', 'storage', 'task_queue', 'utils'], {'lang': 'es6'}); +goog.addDependency('../../../../src/6_branch.js', ['Branch'], ['Logger', 'Server', 'banner', 'branch_view', 'config', 'goog.json', 'journeys_utils', 'resources', 'safejson', 'session', 'storage', 'task_queue', 'utils'], {'lang': 'es6'}); goog.addDependency('../../../../src/7_initialization.js', ['branch_instance'], ['Branch', 'config']); +goog.addDependency('../../../../src/8_logger.js', ['Logger'], [], {'lang': 'es6'}); goog.addDependency('../../../../src/branch_view.js', ['branch_view'], ['banner_css', 'journeys_utils', 'safejson', 'utils']); goog.addDependency('../../../../src/extern.js', [], []); goog.addDependency('../../../../src/journeys_utils.js', ['journeys_utils'], ['banner_utils', 'safejson', 'utils'], {'lang': 'es6'}); @@ -22,8 +23,9 @@ goog.addDependency('../../../../test/1_utils.js', [], ['utils'], {'lang': 'es6'} goog.addDependency('../../../../test/2_storage.js', [], ['storage']); goog.addDependency('../../../../test/3_api.js', [], ['Server', 'config', 'resources', 'safejson', 'storage', 'utils']); goog.addDependency('../../../../test/6_branch.js', [], ['Branch', 'banner_html', 'banner_utils', 'config', 'goog.json', 'resources', 'safejson', 'session', 'storage', 'utils']); -goog.addDependency('../../../../test/6_branch_new.js', [], ['Branch', 'task_queue', 'utils'], {'lang': 'es6'}); +goog.addDependency('../../../../test/6_branch_new.js', [], ['Branch', 'Logger', 'task_queue', 'utils'], {'lang': 'es6'}); goog.addDependency('../../../../test/7_integration.js', [], ['config', 'goog.json']); +goog.addDependency('../../../../test/8_logger.js', [], ['Logger'], {'lang': 'es6'}); goog.addDependency('../../../../test/blob-banner.js', [], []); goog.addDependency('../../../../test/blob-interstitial.js', [], []); goog.addDependency('../../../../test/journeys.js', [], ['Branch', 'banner_utils', 'branch_view', 'config', 'goog.json', 'resources', 'session', 'storage', 'utils'], {'lang': 'es5'});