Simple logging api inspired by Python logging module
authorAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Mon, 30 Dec 2013 12:09:55 +0000 (13:09 +0100)
committerAleksander Łukasz <aleksander.lukasz@nowoczesnapolska.org.pl>
Mon, 30 Dec 2013 12:09:55 +0000 (13:09 +0100)
src/fnpjs/logging/handlers.js [new file with mode: 0644]
src/fnpjs/logging/logging.js [new file with mode: 0644]
src/fnpjs/logging/logging.test.js [new file with mode: 0644]

diff --git a/src/fnpjs/logging/handlers.js b/src/fnpjs/logging/handlers.js
new file mode 100644 (file)
index 0000000..c15d17f
--- /dev/null
@@ -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 (file)
index 0000000..e68302d
--- /dev/null
@@ -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 (file)
index 0000000..5876f5a
--- /dev/null
@@ -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');
+    });
+});
+
+});