Skip to content

Instantly share code, notes, and snippets.

@theladyjaye
Last active December 21, 2015 18:19
Show Gist options
  • Save theladyjaye/6346352 to your computer and use it in GitHub Desktop.
Save theladyjaye/6346352 to your computer and use it in GitHub Desktop.
Simple port of python's std logging module.
define(function(require, exports, module){
var test = {};
var _ = require('underscore'),
_loggers = {},
_levelNames = {
0: 'NOTSET',
1: 'TRACE',
2: 'DEBUG',
3: 'INFO',
4: 'WARN',
5: 'ERROR',
NOTSET: 0,
TRACE: 1,
DEBUG: 2,
INFO: 3,
WARN: 4,
ERROR: 5};
var PlaceHolder = function(options){
var logger = options.logger;
this.loggerMap = {};
this.loggerMap[logger] = null;
};
_.extend(PlaceHolder.prototype, {
append: function(logger){
if (!_.has(this.loggerMap, logger)){
this.loggerMap[logger] = null;
}
}
});
var Logger = function(options){
var level = options.level || _levelNames.NOTSET;
options.level = _checkLevel(level);
_.extend(this, options);
var noop = function(){};
this.trace = noop;
this.debug = noop;
this.info = noop;
this.warn = noop;
this.error = noop;
};
_.extend(Logger.prototype, {
setLevel: function(level){
this.level = _checkLevel(level);
var prefix = this.name;
_.each(_loggers, function(v, k){
if (k.indexOf(prefix) === 0 && v instanceof Logger){
_applyLevelLoggers(v);
}
});
},
isEnabledFor: function(level){
return level >= this.getEffectiveLevel();
},
getEffectiveLevel: function(){
var logger = this;
while(logger){
if(logger.level){
return logger.level;
}
logger = logger.parent;
}
return 0;
}
});
function config(data){
_.each(data.loggers, function(v, k){
var logger = getLogger(k);
logger.setLevel(v.level);
});
}
function getLogger(name){
name = name || 'root';
var result = null;
if (_.has(_loggers, name)){
result = _loggers[name];
if (result instanceof PlaceHolder){
var placeholder = result;
result = new Logger({name: name});
_loggers[name] = result;
_applyChildren(placeholder, result);
_applyParents(result);
//_applyLevelLoggers(result);
}
} else {
result = new Logger({name: name});
_loggers[name] = result;
_applyParents(result);
//_applyLevelLoggers(result);
}
return result;
}
function _applyLevelLoggers(logger){
var maxLevel = _levelNames.ERROR;
var noop = function(){};
while (maxLevel > 0){
var name = _levelNames[maxLevel].toLowerCase();
if (logger.isEnabledFor(maxLevel)){
logger[name] = _consoleMethod(name);
} else {
logger[name] = noop;
}
maxLevel--;
}
}
function _applyChildren(placeholder, logger){
var name = logger.name,
namelen = name.length;
_.each(placeholder.loggerMap, function(v, c){
if(c.parent.name.substring(0, namelen) != name){
logger.parent = c.parent;
c.parent = logger;
}
});
}
function _applyParents(logger){
var name = logger.name,
i = name.lastIndexOf('.'),
parts = name.substring(0, i).split('.'),
result = null;
while (parts.length > 0 && !result){
var substr = parts.join('.');
if(!_.has(_loggers, substr)){
_loggers[substr] = new PlaceHolder({logger:logger});
} else {
var obj = _loggers[substr];
if (obj instanceof Logger){
result = obj;
} else {
if(obj instanceof PlaceHolder){
obj.append(logger);
}
}
}
parts.pop();
}
if (!result){
result = _loggers.root;
}
logger.parent = result;
}
function _checkLevel(level){
// is int?
if (typeof level === 'number' && level % 1 === 0){
return level;
}
return _levelNames[level.toUpperCase()];
}
// the following 2 functions courtesy of:
// https://github.com/pimterry/loglevel/blob/master/lib/loglevel.js#L23-L46
function _consoleMethod(name){
var noop = function () {};
if (typeof console === 'undefined') {
return noop;
} else if (console[name] === undefined) {
return _bindToConsole(console, 'log') || noop;
} else {
return _bindToConsole(console, name);
}
}
function _bindToConsole(console, methodName) {
var method = console[methodName];
if (method.bind === undefined) {
if (Function.prototype.bind === undefined) {
return function() {
method.apply(console, arguments);
};
} else {
return Function.prototype.bind(console);
}
} else {
return method.bind(console);
}
}
_loggers.root = new Logger({'name': 'root', 'level':'notset'});
module.exports.getLogger = getLogger;
module.exports.config = config;
});
@theladyjaye
Copy link
Author

Usage

var log = logging.getLogger('foo.bar');
log.info('Hello %s', 'world');

You will get this on your console:

Hello World 1020 

Logs are in a hierarchy, so if the logger you request does not have a level set, it will inherit the level from it's parent:

var log1 = logging.getLogger('foo');
log1.setLevel('warn');

var log2 = logging.getLogger('foo.bar');
log2.info('Hello %s', 'world');

The above will log nothing, we did not set a level on log2 so it inherited the level from log1, in this case warn, so our log of info will not pass.

Calling logging.getLogger() with no arguments will return the root logger. With the inherited level system in play you can do this:

var log1 = logging.getLogger();
log1.setLevel('info');

//... elsewhere in the application ....

var log2 = logging.getLogger('foo.bar');
log2.debug('Shucks, i\'m a debug and not an info level, so I won\'t go anywhere unless someone sets my level to debug');

You can also centralize your config for all of your loggers:

var logging = require('logging');
    logging.config({
        loggers:{
            'foo': {level:'debug'},
            'foo.bar': {level:'debug'},
            'foo.core.stuff': {level: 'warn'}
        }
    });

Enabling you to tune the logging noise activating and deactivating logging as you need.

Because the loggers exist in a hierarchy, you can apply a configuration to everything with the config as well instead of being granular:

var logging = require('logging');
    logging.config({
        loggers:{
            'foo': {level:'warn'},
        }
    });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment