From a4e92f85f83046cef29d4ce0f71cea6250ce8df6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aleksander=20=C5=81ukasz?= Date: Mon, 30 Dec 2013 13:09:55 +0100 Subject: [PATCH] Simple logging api inspired by Python logging module --- src/fnpjs/logging/handlers.js | 8 ++ src/fnpjs/logging/logging.js | 81 +++++++++++++++++++ src/fnpjs/logging/logging.test.js | 128 ++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 src/fnpjs/logging/handlers.js create mode 100644 src/fnpjs/logging/logging.js create mode 100644 src/fnpjs/logging/logging.test.js diff --git a/src/fnpjs/logging/handlers.js b/src/fnpjs/logging/handlers.js new file mode 100644 index 0000000..c15d17f --- /dev/null +++ b/src/fnpjs/logging/handlers.js @@ -0,0 +1,8 @@ +define(function() { + +'use strict'; + + +return {}; + +}); \ No newline at end of file diff --git a/src/fnpjs/logging/logging.js b/src/fnpjs/logging/logging.js new file mode 100644 index 0000000..e68302d --- /dev/null +++ b/src/fnpjs/logging/logging.js @@ -0,0 +1,81 @@ +define(function(require) { + +'use strict'; + +var _ = require('libs/underscore'), + handlers = require('fnpjs/logging/handlers'), + config = {}, + levels = ['debug', 'info', 'warning', 'error', 'critical']; + + +var Logger = function(name) { + this.name = name; + Object.defineProperty(this, 'config', { + get: function() { + return _.extend({ + propagate: true, + level: 'warn', + handlers: [] + }, config.loggers[name] || {}); + } + }); +}; + +_.extend(Logger.prototype, { + log: function(level, message, data) { + if(levels.indexOf(level) !== -1 && levels.indexOf(level) >= levels.indexOf(this.config.level)) { + this.config.handlers.forEach(function(handlerName) { + var handlerConfig = config.handlers[handlerName], + handler = handlerConfig.handler, + handlerLevel = handlerConfig.level || 'info'; + + if(typeof handler === 'string') { + handler = handlers[handlerConfig.handler]; + } + if(!handler) { + throw new Error('Unknown handler: ' + handlerName); + } + + if(levels.indexOf(handlerLevel) !== -1 && levels.indexOf(level) >= levels.indexOf(handlerLevel)) { + handler(message, level, data); + } + }); + } + if(this.config.propagate && this.name) { + var logger = new Logger(this.name.split('.').slice(0, -1).join('.')); + logger.log(level, message, data); + } + }, + exception: function(e) { + this.log('error', e.toString(), {exception: e}); + } +}); + +levels.forEach(function(level) { + Logger.prototype[level] = function(message, data) { + return this.log(level, message, data); + }; +}); + + +var api = { + getLogger: function(name) { + return new Logger(name); + }, + setConfig: function(_config) { + config = _.extend({ + handlers: [], + loggers: [] + } ,_config); + }, + clearConfig: function() { + this.setConfig({}); + } +}; + +api.clearConfig(); + + +return api; + +}); diff --git a/src/fnpjs/logging/logging.test.js b/src/fnpjs/logging/logging.test.js new file mode 100644 index 0000000..5876f5a --- /dev/null +++ b/src/fnpjs/logging/logging.test.js @@ -0,0 +1,128 @@ +define(function(require) { + +'use strict'; +/* global describe, it, beforeEach */ +var chai = require('libs/chai'), + logging = require('./logging.js'), + expect = chai.expect; + + +// Global log object for defining expectations +var log = { + _msgs: [], + clear: function() { + this._msgs = []; + }, + append: function(msg) { + this._msgs.push(msg); + }, + contains: function(msg) { + return this._msgs.indexOf(msg) !== -1; + }, + getLast: function() { + return this._msgs.length ? this._msgs[this._msgs.length] : undefined; + }, + isEmpty: function() { + return this._msgs.length === 0; + }, + getMessages: function() { + return this._msgs; + } +}; + +// Loggin handler that just writes to the global logger object +var testLoggingHandler = function(msg) { + log.append(msg); +}; + +describe('Logging', function() { + + + beforeEach(function() { + log.clear(); + logging.clearConfig(); + }); + + var setConfig = function(loggerLevel, handlerLevel) { + logging.setConfig({ + handlers: { + testHandler: { + handler: testLoggingHandler, + level: handlerLevel + } + }, + loggers: { + '': { + level: loggerLevel, + handlers: ['testHandler'] + } + } + }); + }; + + it('works with sample config', function() { + setConfig('debug', 'debug'); + var logger = logging.getLogger('some.name'); + logger.debug('debug msg'); + expect(log.contains('debug msg')).to.equal(true); + }); + + it('filters level on loggers', function() { + setConfig('info', 'debug'); + var logger = logging.getLogger('some.name'); + logger.debug('debug msg'); + expect(log.isEmpty()).to.equal(true, 'debug message filtered out'); + logger.info('info msg'); + expect(log.contains('info msg')).to.equal(true, 'info message passed'); + }); + + it('filters level on handlers', function() { + setConfig('debug', 'info'); + var logger = logging.getLogger('some.name'); + logger.debug('debug msg'); + expect(log.isEmpty()).to.equal(true, 'debug message filtered out'); + logger.info('info msg'); + expect(log.contains('info msg')).to.equal(true, 'info message passed'); + }); + + it('propagates message to upper logger depending on the propagate flag', function() { + var config = { + handlers: { + testHandler: { + handler: testLoggingHandler, + level: 'debug' + } + }, + loggers: { + '': { + level: 'debug', + handlers: ['testHandler'] + }, + 'logger1': { + level: 'debug', + handlers: ['testHandler'] + } + } + }, + logger; + + config.loggers.logger1.propagate = false; + logging.setConfig(config); + + logger = logging.getLogger('logger1'); + + logger.debug('msg1'); + expect(log.contains('msg1')).to.equal(true, 'first message logged'); + expect(log.getMessages().length === 1).to.equal(true, 'logger didn\'t propagate its message'); + + log.clear(); + config.loggers.logger1.propagate = true; + logging.setConfig(config); + + logger.debug('msg2'); + expect(log.contains('msg2')).to.equal(true, 'second message logged'); + expect(log.getMessages().length === 2).to.equal(true, 'second message propagated to upper logger'); + }); +}); + +}); -- 2.20.1