Source: tools/logger.js


define(['mmirf/stacktrace', 'mmirf/util/toArray', 'module'],
/**
 * A Logger factory.<br>
 *
 * @example
 *  //use logger
 *  var Logger = mmir.require('mmirf/logger');
 *  var log = Logger.create('example');
 *
 *  if(log.isVerbose()) log.debug('test');//will write the message to debug-console)
 *  log.error(new Error());//will write the error (including its stack) to error console)
 *
 *  //example for setting up a logger in a requirejs-module:
 *  define(['mmirf/logger', 'module'], function(Logger, module){
 *
 *    var logger = Logger.create(module);
 *    //this would create the same logger-object:
 *    //  Logger.create(module.id, module.config(module).logLevel);
 *
 *    //use the logger instance...
 *
 *    //create / retrieve the same logger
 *    var sameLogger = Logger.create(module.id);
 *
 *  });
 *
 * @class
 * @name Logging
 * @memberOf mmir
 * @static
 * @hideconstructor
 *
 * @see mmir.tools.Logger
 *
 */
function(stacktrace, toArray, module){

/**
 * created loggers:
 * loggerId (string) -> Logger
 * @type {Map<string, mmir.tools.Logger>}
 * @private
 */
var _loggers = new Map();
/**
 * pre-configured/"default" log-levels when creating loggers:
 * number -> loggerId (string)
 * @type {Map<number, string>}
 * @private
 */
var _logLevels = new Map();

/**
 * the (global) logging level
 *
 * 0: verbose
 * 1: debug
 * 2: info
 * 3: warn
 * 4: error
 * 5: critical
 * 6: disabled
 *
 * @memberOf mmir.Logging#
 */
var _level = 1;

/**
 * @private
 * @type String
 * @memberOf mmir.Logging#
 */
var tmpLogLevel = module.config(module).logLevel;
if(typeof tmpLogLevel !== 'undefined'){
	if(tmpLogLevel && typeof tmpLogLevel === 'object'){
		if(tmpLogLevel.levels){
			parseLogLevels(tmpLogLevel.levels, 'levels');
		}
		if(tmpLogLevel.modules){
			parseLogLevels(tmpLogLevel.modules, 'modules');
		}
		tmpLogLevel = typeof tmpLogLevel.level !== 'undefined'? tmpLogLevel.level : 'debug';
	}
	if(typeof tmpLogLevel !== 'number'){
		tmpLogLevel = getAsLevel(tmpLogLevel);
	}
	_level = tmpLogLevel;
}

function parseLogLevels(opt, mode){
	if(mode === 'levels'){
		// [field]: Array<moduleId>
		var list, level;
		for(var l in opt){
			if(opt.hasOwnProperty(l)){
				list = opt[l];
				level = parseLogLevel(l);
				for(var i=0, size=list.length; i < size; ++i){
					setLogLevel(list[i], level);
				}
			}
		}
	} else if(mode === 'modules'){
		// [moduleId]: logLevel
		for(var modId in opt){
			if(opt.hasOwnProperty(modId)){
				setLogLevel(modId, opt[modId]);
			}
		}
	} else {
		print('logger', getAsLevel('error'), 'parseLogLevels(): invalid mode '+mode);
	}
}

function parseLogLevel(logLevel){
	if(typeof logLevel === 'string'){
		var num = parseFloat(logLevel);
		if(isFinite(num)){
			return num;
		}
	}
	if(typeof logLevel !== 'number'){
		return getAsLevel(logLevel);
	}
	return logLevel;
}


/**
 * Get the log-level as number.
 *
 * @param {String} strLogLevel
 * 			the string representation for the log-level
 * @returns {Number}
 * 				the log-level as number
 * @private
 * @memberOf mmir.tools.Logger.prototype
 *
 * @see #getLevel
 */
function getAsLevel(strLogLevel){
	if(typeof strLogLevel === 'string'){
		var str = strLogLevel.toLowerCase();
		if(str === 'verbose'){
			return 0;
		} else if(str === 'debug'){
			return 1;
		} else if(str === 'info'){
			return 2;
		} else if(str === 'warn'){
			return 3;
		} else if(str === 'error'){
			return 4;
		} else if(str === 'critical'){
			return 5;
		} else if(str === 'disabled'){
			return 6;
		} else
			throw new Error('Logger.getAsLevel: unknown parameter value "'+strLogLevel+'"');
	}

	throw new TypeError('Logger.getAsLevel: parameter must be number or string, but "'+strLogLevel+'" is '+typeof strLogLevel);
}

/**
 * configure default log-level for a logger:
 * in case the logger is created without specifying a log-level, this pre-configured
 * log-level will be used.
 *
 * @param {String} loggerId
 * 			the ID for the logger
 * @param {Number|String} logLevel
 * 				the log-level for the logger
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function setLogLevel(loggerId, logLevel){
	if(typeof loggerId !== 'string'){
		loggerId = loggerId + '';
	}
	if(typeof logLevel !== 'number'){
		logLevel = getAsLevel(logLevel);
	}
	_logLevels.set(loggerId, logLevel);
}

/**
 * print log message with error information.
 *
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function printe(loggerName, logLevel, className, funcName, msg, error, reverseCallStack){

	if( isErr(className)){
		error = className;
		className = '';
	} else if( isErr(funcName) ){
		error = funcName;
		funcName = void(0);
		if(typeof className === 'undefined'){
			className = '';
		}
	} else if( isErr(msg) ){
		error = msg;
		msg = void(0);
		if(typeof className === 'undefined'){
			if(typeof funcName === 'undefined'){
				className = '';
			}
			else {
				className = funcName;
				funcName = void(0);
			}
		}
	}

	print(loggerName, logLevel, createErr(createMsg(className, funcName, msg), error), reverseCallStack);
}

/**
 * creates the message text.
 *
 * @returns {string} the message text
 *
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function createMsg(className, funcName, msg){

	var out;

	if(className){
		if(funcName && typeof funcName !== 'number'){
			if(msg && typeof funcName !== 'number'){
				out = className+'.'+funcName+': '+msg;
			} else {

//					if(arguments.callee !== 'undefined'){
//						out('callee: '+arguments.callee());
//					}

				out = className+': '+funcName;
			}
		} else {

//				if(arguments.callee !== 'undefined'){
//					out('callee: '+arguments.callee());
//				}

			out = className;
		}
	} else {

//			if(arguments.callee !== 'undefined'){
//				out('callee: '+arguments.callee());
//			}

		if(typeof className === 'undefined'){
			out = 'UNDEFINED';
		} else if(typeof className !== 'string'){
			out = Object.prototype.toString.call(null, className);
		} else {
			out = className;
		}
	}

	return out;
}

/**
 * HELPER: check if errObj is an Error
 *
 * @returns {Boolean}
 *
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function isErr(errObj){

	//TODO also do feature detection for error-like objects?
	return errObj instanceof Error;
}

/**
 * Creates error message (with stack trace, if possible).
 *
 * @returns {string} the error message
 *
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function createErr(msg, error){

	var err ='';
	var errMsg = '';
	var re;
	if(error){

		if(error.name){
			err = '<'+error.name+'> ';
		}

		if(error.number){
			err += '#'+error.number+' ';
		}

		if(error.stack){

			errMsg = error.stack;

			if(error.name && (re = new RegExp('^'+error.name)).test(errMsg)){
				errMsg = errMsg.replace(re, '');
			}

		} else {

			if(error.message){
				errMsg = ' - ' + error.message;
			}

			if(error.description){
				errMsg = ' - ' + error.description;
			}

			if(error.fileName){

				var lineNo = '';

				if(error.lineNumber){
					lineNo = ', line ' + error.lineNumber;
				}

				errorMsg += ' ('+error.fileName+lineNo+')';
			}
		}

	}

	return err+msg+errMsg;
}

//in some environments console.debug may not exits -> use console.log instead
var DEBUG_FUNC_NAME = console.debug? 'debug' : 'log';

/**
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function print(loggerName, logLevel, msg){
	var prefix, func;
	switch(logLevel){
	case 0:
		prefix = '[VERBOSE] ';
		func = 'log';
		break;
	case 1:
		prefix = '[DEBUG] ';
		func = DEBUG_FUNC_NAME;
		break;
	case 2:
		prefix = '[INFO] ';
		func = 'info';
		break;
	case 3:
		prefix = '[WARN] ';
		func = 'warn';
		break;
	case 4:
		prefix = '[ERROR] ';
		func = 'error';
		break;
	case 5:
		prefix = '[CRITICAL] ';
		func = 'error';
		break;
	case 6:		//debug-level "disabled" -> do nothing
		return; /////////////////// EARLY EXIT //////////
	default:
		prefix = '[UNDEF_LOG_LEVEL_'+logLevel+'] ';
		func = 'log';
		break;
	}
	console[func](prefix + loggerName + msg);
}

/**
 * quasi constant:
 * is updated once, if stacktrace is enabled -- otherwise readonly!
 *
 * @private
 * @type boolean
 * @memberOf mmir.tools.Logger.prototype
 */
var IS_TRACE = false;

/**
 * if stacktrace is enabled:
 * number of stack-levels that should be omitted (to hide logger-internal function-call-stack)
 *
 * @private
 * @type number
 * @const
 * @memberOf mmir.tools.Logger.prototype
 */
var STACK_LEVEL = 2;

/**
 * if stacktrace is enabled:
 * number of stack-levels that should be omitted when printing an error via {@link #printe}
 * (to hide logger-internal function-call-stack)
 *
 * NOTE:  if isErrInvoked is TRUE: this function was invoked via additional printe()-call
 *        => need to take 1 more step down the stack
 *
 * @private
 * @type number
 * @const
 * @memberOf mmir.tools.Logger.prototype
 */
var ERROR_STACK_LEVEL = STACK_LEVEL + 1;


/**
 * HELPER for getting the stack-level/-line from which on the trace should be printed
 *        in order to suppress showing internal function calls when generating the log output
 *
 * @returns {number} the stack-level/-line from which on the trace should be printed
 *
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function getStackLevel(_className, _funcName, _msg, _error, _reverseCallStack, isPrinte){

	if(!IS_TRACE){
		return 0;
	}

	var level = isPrinte? ERROR_STACK_LEVEL : STACK_LEVEL;
	for(var arg, i = 0, size = arguments.length; i < size; ++i){
		arg = arguments[i];
		if(typeof arg === 'number'){
			return arg + level;
		}
	}
	return level;
}

/**
 * HELPER for modifying the reverseCallStack argument in arguments
 *
 * @returns {number} the stack-level/-line from which on the trace should be printed
 *
 * @param  {Arguments} args the function's original arguments object
 * @param  {number} offset the offset for the call-stack, value 0 would mean no change to the callstack (i.e. "go further back by [offset]")
 * @return {Array} the converted arguments object with applied (or modified) reverseCallStack argument
 *
 * @private
 * @memberOf mmir.tools.Logger.prototype
 */
function applyReverseCallStackWith(args, offset){
	var list = toArray(args);
	if(offset === 0){
		return list;
	}
	var len = list.length;
	if(typeof list[len-1] === 'number'){
		list[len-1] += offset;
	} else {
		list[len] = offset;
	}
	return list;
}


//enable tracing?
/**
 * configuration value for enabling/disabling tracing in log-output
 * @private
 * @type Object
 * @memberOf mmir.tools.Logger.prototype
 */
var tmpTraceConfig = module.config(module).trace;
if(tmpTraceConfig !== false || (tmpTraceConfig !== null && typeof tmpTraceConfig === 'object' && tmpTraceConfig.trace !== false)){
	IS_TRACE = true;

	/**
	 * options object for tracing
	 * @private
	 * @type Object
	 * @memberOf mmir.tools.Logger.prototype
	 */
	var pnTraceOptions = tmpTraceConfig === true? void(0) : tmpTraceConfig;
	/**
	 * setting for trace-depth (i.e. stack-depth)
	 * @private
	 * @type Boolean
	 * @memberOf mmir.tools.Logger.prototype
	 */
	var isFullStackDepth = pnTraceOptions && pnTraceOptions.depth === "full";

	/**
	 * proxy object for storing the original implementation
	 * of {@link mmir.tools.Logger#print} function.
	 *
	 * (only used, if tracing is enabled!)
	 *
	 * @private
	 * @type Function
	 * @memberOf mmir.tools.Logger.prototype
	 */
	var pnOriginal = print;

	//do enable tracing: append stacktrace to messages in print-function
	if(isFullStackDepth){

		//NOTE code duplication for the sake of a more efficient print function

		/**
		 * Extension for {@link mmir.tools.Logger#print} function with tracing.
		 *
		 * This extension prints the full stack trace in log-output.
		 *
		 * (only used, if tracing is enabled!)
		 *
		 * @private
		 * @name printFullStack
		 * @function
		 * @memberOf mmir.tools.Logger.prototype
		 */
		print = function printFullStack(loggerName, logLevel, msg, stackLevel){
			if(typeof msg === 'undefined' || msg === null){
				msg = '';
			}
			//if isErrInvoked is TRUE: this function was invoked via additional printe()-call
			// => need to take 1 more step down the stack
			//if isErrInvoked is a number: use
			msg += '\n  ' + stacktrace.getSync(pnTraceOptions).slice(stackLevel).join('\n  ');
			pnOriginal.call(this, loggerName, logLevel, msg);
		};
	}
	else {

		//NOTE code duplication for the sake of a more efficient print function

		/**
		 * Extension for {@link mmir.tools.Logger#print} function with tracing.
		 *
		 * This extension prints only the first entry of the stack trace in log-output.
		 *
		 * (only used, if tracing is enabled!)
		 *
		 * @private
		 * @name printStack
		 * @function
		 * @memberOf mmir.tools.Logger.prototype
		 */
		print = function printStack(loggerName, logLevel, msg, stackLevel){
			if(typeof msg === 'undefined' || msg === null){
				msg = '';
			}
			//if isErrInvoked is TRUE: this function was invoked via additional printe()-call
			// => need to take 1 more step down the stack
			msg += '\n  ' + stacktrace.getSync(pnTraceOptions)[stackLevel];
			pnOriginal.call(this, loggerName, logLevel, msg);
		};
	}

}

/**
 * Constructor-Method of Class Logger<br>
 * @constructor
 * @class
 * @name Logger
 * @memberOf mmir.tools
 *
 * @param {String} theName
 * 					the name / ID for the logger
 * @param {String|Number} [theLevel] OPTIONAL
 * 					the log-level.
 * 					If omitted, the logger will use
 * 					the default log-level
 *
 * @see #getAsLevel
 * @see #getLevel
 */
function Logger(theName, theLevel){

	//the name (/key) for the logger instance
	this.name = '';
	if(typeof theName !== 'undefined'){
		this.name = '<'+theName+'> ';
	}

	if(typeof theLevel !== 'undefined'){
		if(typeof theLevel !== 'number'){
			theLevel = getAsLevel(theLevel);
		}

		this.level = theLevel;
	}
}

Logger.prototype =
/** @lends mmir.tools.Logger# */
{//public instance members

	/**
	 * Get the current log-level:
	 * if a specific log-level for this Logger instance is set,
	 * this value is returned.
	 * Otherwise, the default log-level as returned by {@link mmir.Logging#getDefaultLogLevel}
	 * is used.
	 *
	 * Log-levels:
	 * <ul>
	 * 	<li>0: verbose</li>
	 * 	<li>1: debug</li>
	 * 	<li>2: info</li>
	 * 	<li>3: warn</li>
	 * 	<li>4: error</li>
	 * 	<li>5: critical</li>
	 * 	<li>6: disabled</li>
	 * <ul>
	 *
	 * @returns {Number} the logging level
	 *
	 * @see #setLevel
	 */
	getLevel : function(){

		if(typeof this.level !== 'undefined'){
			return this.level;
		}

		//return default/global logging-level:
		return _level;
	},
	/**
	 * Set the logging level.
	 *
	 * @param {String|Number} loggingLevel
	 * 						if {Number} the logging level as a number
	 *                      if {String} the logging level as a string (see {@link #getLevel})
	 *
	 * @see #getLevel
	 */
	setLevel : function(loggingLevel){

		if(typeof loggingLevel !== 'number'){
			loggingLevel = getAsLevel(loggingLevel);
		}

		this.level = loggingLevel;
	},
	/**
	 *
	 * Print a log message, if at least <code>debug</code> log-level is set.
	 *
	 * @param {String} [className] OPTIONAL
	 * 			the name of the class/object from which the logging is invoked from
	 * @param {String} [funcName] OPTIONAL
	 * 			the name of the function (within the class) from which the logging is invoked from
	 * @param {String} msg
	 * 			the log message
	 * @param {Error} [error] OPTIONAL
	 * 			an error object: if available, its message and error-stack will be print to the output
	 * @param {number} [reverseCallStack] OPTIONAL
	 * 			offset when printing the callstack position/information: a positive number will go further back/up the callstack.
	 * 			This can be used in logging-helper functions to select the invoking function's position from the callstack instead of
	 * 			printing the information of the logging-helper function itself.
	 * @public
	 */
	log: function(className, funcName, msg, error, reverseCallStack){
		if(this.isDebug()){
			printe(this.name, 1 /*getAsLevel('debug')*/, className, funcName, msg, error, getStackLevel(className, funcName, msg, error, reverseCallStack, true));
		}
	},

	/**
	 *
	 * Print a <em>verbose</em> log message, if at least <code>verbose</code> (0) log-level is set.
	 *
	 * @param {String} [className] OPTIONAL
	 * 			the name of the class/object from which the logging is invoked from
	 * @param {String} [funcName] OPTIONAL
	 * 			the name of the function (within the class) from which the logging is invoked from
	 * @param {String} msg
	 * 			the log message
	 * @param {number} [reverseCallStack] OPTIONAL
	 * 			offset when printing the callstack position/information: a positive number will go further back/up the callstack.
	 * 			This can be used in logging-helper functions to select the invoking function's position from the callstack instead of
	 * 			printing the information of the logging-helper function itself.
	 * @public
	 */
	//TODO implement/add helpers for file-logging (+ CSV data helpers etc)
	verbose : function(className, funcName, msg, reverseCallStack){
		if(this.isVerbose()){
			print( this.name, 0 /*getAsLevel('verbose')*/, createMsg(className, funcName, msg), getStackLevel(className, funcName, msg, null, reverseCallStack, false));
		}
	},

	/**
	 *
	 * Print a <em>debug</em> log message, if at least <code>debug</code> (1) log-level is set.
	 *
	 * @param {String} [className] OPTIONAL
	 * 			the name of the class/object from which the logging is invoked from
	 * @param {String} [funcName] OPTIONAL
	 * 			the name of the function (within the class) from which the logging is invoked from
	 * @param {String} msg
	 * 			the log message
	 * @param {number} [reverseCallStack] OPTIONAL
	 * 			offset when printing the callstack position/information: a positive number will go further back/up the callstack.
	 * 			This can be used in logging-helper functions to select the invoking function's position from the callstack instead of
	 * 			printing the information of the logging-helper function itself.
	 * @public
	 */
	debug : function(className, funcName, msg, reverseCallStack){
		if(this.isDebug()){
			print( this.name, 1 /*getAsLevel('debug')*/, createMsg(className, funcName, msg), getStackLevel(className, funcName, msg, null, reverseCallStack, false));
		}
	},

	/**
	 *
	 * Print an <em>information</em> log message, if at least <code>info</code> (2) log-level is set.
	 *
	 * @param {String} [className] OPTIONAL
	 * 			the name of the class/object from which the logging is invoked from
	 * @param {String} [funcName] OPTIONAL
	 * 			the name of the function (within the class) from which the logging is invoked from
	 * @param {String} msg
	 * 			the log message
	 * @param {number} [reverseCallStack] OPTIONAL
	 * 			offset when printing the callstack position/information: a positive number will go further back/up the callstack.
	 * 			This can be used in logging-helper functions to select the invoking function's position from the callstack instead of
	 * 			printing the information of the logging-helper function itself.
	 * @public
	 */
	info : function(className, funcName, msg, reverseCallStack){
		if(this.isInfo()){
			print( this.name, 2 /*getAsLevel('info')*/, createMsg(className, funcName, msg), getStackLevel(className, funcName, msg, null, reverseCallStack, false));
		}
	},

	/**
	 *
	 * Print a <em>warning</em> log message, if at least <code>warn</code> (3) log-level is set.
	 *
	 * @param {String} [className] OPTIONAL
	 * 			the name of the class/object from which the logging is invoked from
	 * @param {String} [funcName] OPTIONAL
	 * 			the name of the function (within the class) from which the logging is invoked from
	 * @param {String} msg
	 * 			the log message
	 * @param {number} [reverseCallStack] OPTIONAL
	 * 			offset when printing the callstack position/information: a positive number will go further back/up the callstack.
	 * 			This can be used in logging-helper functions to select the invoking function's position from the callstack instead of
	 * 			printing the information of the logging-helper function itself.
	 * @public
	 */
	warn : function(className, funcName, msg, reverseCallStack){
		if(this.isWarn()){
			print( this.name, 3 /*getAsLevel('warn')*/, createMsg(className, funcName, msg), getStackLevel(className, funcName, msg, null, reverseCallStack, false));
		}
	},

	/**
	 *
	 * Print an <em>error</em> log message, if at least <code>error</code> (4) log-level is set.
	 *
	 * @param {String} [className] OPTIONAL
	 * 			the name of the class/object from which the logging is invoked from
	 * @param {String} [funcName] OPTIONAL
	 * 			the name of the function (within the class) from which the logging is invoked from
	 * @param {String} msg
	 * 			the log message
	 * @param {Error} [error] OPTIONAL
	 * 			an error object: if available, its message and error-stack will be print to the output
	 * @param {number} [reverseCallStack] OPTIONAL
	 * 			offset when printing the callstack position/information: a positive number will go further back/up the callstack.
	 * 			This can be used in logging-helper functions to select the invoking function's position from the callstack instead of
	 * 			printing the information of the logging-helper function itself.
	 * @public
	 */
	error : function(className, funcName, msg, error, reverseCallStack){
		if(this.isError()){
			printe(this.name, 4 /*getAsLevel('error')*/, className, funcName, msg, error, getStackLevel(className, funcName, msg, error, reverseCallStack, true));
		}
	},

	/**
	 *
	 * Print a <em>critical</em> (exception) log message, if at least <code>critical</code> (5) log-level is set.
	 *
	 * @param {String} [className] OPTIONAL
	 * 			the name of the class/object from which the logging is invoked from
	 * @param {String} [funcName] OPTIONAL
	 * 			the name of the function (within the class) from which the logging is invoked from
	 * @param {String} msg
	 * 			the log message
	 * @param {Error} [error] OPTIONAL
	 * 			an error object: if available, its message and error-stack will be print to the output
	 * @param {number} [reverseCallStack] OPTIONAL
	 * 			offset when printing the callstack position/information: a positive number will go further back/up the callstack.
	 * 			This can be used in logging-helper functions to select the invoking function's position from the callstack instead of
	 * 			printing the information of the logging-helper function itself.
	 * @public
	 */
	critical : function(className, funcName, msg, error, reverseCallStack){
		if(this.isCritical()){
			printe(this.name, 5 /*getAsLevel('critical')*/, className, funcName, msg, error, getStackLevel(className, funcName, msg, error, reverseCallStack, true));
		}
	},

	/**
	 *
	 * Check if the current log-level is at least <code>verbose</code>.
	 *
	 * @returns {Boolean}
	 * 				<code>true</code> if at least log-level <code>verbose</code> (0)
	 * @public
	 *
	 * @see #verbose
	 */
	isVerbose : function(loggerName){
		return this.getLevel() <= 0;// getAsLevel('verbose');
	},

	/**
	 *
	 * Check if the current log-level is at least <code>debug</code>.
	 *
	 * @returns {Boolean}
	 * 				<code>true</code> if at least log-level <code>debug</code> (1)
	 * @public
	 *
	 * @see #debug
	 */
	isDebug : function(loggerName){
		return this.getLevel() <= 1;//getAsLevel('debug');
	},

	/**
	 *
	 * Check if the current log-level is at least <code>info</code>.
	 *
	 * @returns {Boolean}
	 * 				<code>true</code> if at least log-level <code>info</code> (2)
	 * @public
	 *
	 * @see #info
	 */
	isInfo : function(loggerName){
		return this.getLevel() <= 2;//getAsLevel('info');
	},

	/**
	 *
	 * Check if the current log-level is at least <code>warn</code>.
	 *
	 * @returns {Boolean}
	 * 				<code>true</code> if at least log-level <code>warn</code> (3)
	 * @public
	 *
	 * @see #warn
	 */
	isWarn : function(loggerName){
		return this.getLevel() <= 3;//getAsLevel('warn');
	},

	/**
	 *
	 * Check if the current log-level is at least <code>error</code>.
	 *
	 * @returns {Boolean}
	 * 				<code>true</code> if at least log-level <code>error</code> (4)
	 * @public
	 *
	 * @see #error
	 */
	isError : function(loggerName){
		return this.getLevel() <= 4;//getAsLevel('error');
	},

	/**
	 *
	 * Check if the current log-level is at least <code>critical</code>.
	 *
	 * @returns {Boolean}
	 * 				<code>true</code> if at least log-level <code>critical</code> (5)
	 * @public
	 *
	 * @see #critical
	 */
	isCritical : function(loggerName){
		return this.getLevel() <= 5;//getAsLevel('critical');
	},

	/**
	 *
	 * Check if the current log-level is at least <code>disabled</code>.
	 *
	 * @returns {Boolean}
	 * 				<code>true</code> if at least log-level <code>disable</code> (6)
	 * @public
	 *
	 * @see #getLevel
	 */
	isDisabled : function(loggerName){
		return this.getLevel() <= 6;//getAsLevel('disabled');
	}
};

//define alias'

/**
 * Alias for {@link #log}.
 *
 * @public
 * @function l
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#log
 */
Logger.prototype.l = function(){
	return this.log.apply(this, applyReverseCallStackWith(arguments, 1));
};

/**
 * Alias for {@link #verbose}.
 *
 * @public
 * @function v
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#verbose
 */
Logger.prototype.v = function(){
	return this.verbose.apply(this, applyReverseCallStackWith(arguments, 1));
};

/**
 * Alias for {@link #debug}.
 *
 * @public
 * @function d
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#debug
 */
Logger.prototype.d = function(){
	return this.debug.apply(this, applyReverseCallStackWith(arguments, 1));
};

/**
 * Alias for {@link #info}.
 *
 * @public
 * @function i
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#info
 */
Logger.prototype.i = function(){
	return this.info.apply(this, applyReverseCallStackWith(arguments, 1));
};

/**
 * Alias for {@link #warn}.
 *
 * @public
 * @function w
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#warn
 */
Logger.prototype.w = function(){
	return this.warn.apply(this, applyReverseCallStackWith(arguments, 1));
};

/**
 * Alias for {@link #error}.
 *
 * @public
 * @function e
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#error
 */
Logger.prototype.e = function(){
	return this.error.apply(this, applyReverseCallStackWith(arguments, 1));
};

/**
 * Alias for {@link #critical}.
 *
 * @public
 * @function c
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#critical
 */
Logger.prototype.c = function(){
	return this.critical.apply(this, applyReverseCallStackWith(arguments, 1));
};

/**
 * @scope Logger#
 */

/**
 * Alias for {@link #isVerbose}.
 *
 * @public
 * @function isv
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#isVerbose
 */
Logger.prototype.isv = function(){
	return this.isVerbose.apply(this, arguments);
};

/**
 * Alias for {@link #isDebug}.
 *
 * @public
 * @function isd
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#isDebug
 */
Logger.prototype.isd = function(){
	return this.isDebug.apply(this, arguments);
};

/**
 * Alias for {@link #isInfo}.
 *
 * @public
 * @function isi
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#isInfo
 */
Logger.prototype.isi = function(){
	return this.isInfo.apply(this, arguments);
};

/**
 * Alias for {@link #isWarn}.
 *
 * @public
 * @function isw
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#isWarn
 */
Logger.prototype.isw = function(){
	return this.isWarn.apply(this, arguments);
};

/**
 * Alias for {@link #isError}.
 *
 * @public
 * @function ise
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#isError
 */
Logger.prototype.ise = function(){
	return this.isError.apply(this, arguments);
};

/**
 * Alias for {@link #isCritical}.
 *
 * @public
 * @function isc
 * @memberOf mmir.tools.Logger#
 * @copydoc mmir.tools.Logger#isCritical
 */
Logger.prototype.isc = function(){
	return this.isCrictial.apply(this, arguments);
};


/**
 * The default logger:
 * access by invoking {@link mmir.Logging#create} without arguments
 * @private
 * @type mmir.tools.Logger
 * @memberOf mmir.Logging
 * @member _defaultLogger
 * @inner
 * @example
 * var defaultLogger = mmir.require('mmirf/logger').create();
 */
var _defaultLogger = new Logger();
//default logger always has default/global log-level:
/**
 * @copydoc mmir.tools.Logger#getLevel
 * @memberOf mmir.Logging~_defaultLogger
 */
_defaultLogger.getLevel = function(){
	return _level;
};

//the instance for the Logging factory:
var instance =
/** @lends mmir.Logging.prototype */
{//public API

	/**
	 * Creates a {@link mmir.tools.Logger} instance.
	 *
	 * If a logger for <code>loggerName</code> already exists,
	 * the existing logger is returned (instead of creating a new one).
	 *
	 * @param {String|Object} [loggerName]
	 * 			If String: a name / ID for the logger that should be created / retrieved.<br>
	 * 			If Object: an requirejs <code>module</code> object, i.e. should contain properties
	 * 							<code>id</code> (String) which will set the <code>loggerName</code>, and a property/function
	 * 							<code>config</code> (Function) that returns an object with property
	 * 							<code>logLevel</code> (i.e. <code>config().logLevel</code> should be valid).<br>
	 * 			If omitted, the default logger will be returned.
	 * @param {String|LogLevel} [logLevel]
	 * 			a name / ID for the logger that should be created / retrieved.
	 * 			If omitted, the default logger will be returned.
	 *
	 * @returns {mmir.tools.Logger} the created (or retrieved) logger
	 *
	 * @memberOf mmir.Logging.prototype
	 * @public
	 *
	 * @see mmir.tools.Logger
	 * @see mmir.tools.Logger#setLevel
	 */
	create: function(loggerName, logLevel){

			//special argument: is first argument is a (requirejs) module?
			if(typeof loggerName === 'object' && loggerName && loggerName.id){
				//extract parameters from module object:
				if(typeof loggerName.config === 'function'){
					logLevel = loggerName.config(loggerName).logLevel;//<- may be undefined
				} else if(typeof WEBPACK_BUILD !== 'undefined' && WEBPACK_BUILD){
					//NOTE automatic injection by webpack of config-function (into module) does not work here, because loggerName is generic & webpack cannot statically inter its type as Module
					//     -> explicitly use module.config() helper module:
					logLevel = require('build-tool/module-config-helper').config(loggerName).logLevel;//<- may be undefined
				}
				loggerName = loggerName.id;
			}

		//no logger specified: return default logger
		if(! loggerName){
			return _defaultLogger;
		}

		if(typeof loggerName !== 'string'){
			loggerName = loggerName.toString();
		}

		//return specified logger
		var theLogger = _loggers.get(loggerName);
		if(typeof theLogger === 'undefined'){
			if(typeof logLevel === 'undefined'){
				logLevel = _logLevels.get(loggerName);
				// if(typeof logLevel === 'number'){
				// 	console.log('using predefined log level ', logLevel, ' for ', loggerName);
				// }
			}
			//create, if not existing
			var theNewLogger = new Logger(loggerName, logLevel);
			_loggers.set(loggerName, theNewLogger);

			return theNewLogger;
		}

		if(typeof logLevel !== 'undefined'){
			theLogger.setLevel(logLevel);
		}

		return theLogger;
	},
	/**
	 * Sets the default log-level.
	 *
	 * This setting is used by loggers, that do not have
	 * a specific log-level set.
	 *
	 * @param {String|Number} theLevel
	 * 			if NUMBER, the log level as a number between 0 (verbose) and 6 (disabled)
	 * 			if STRING, the logging level as a string (see {@link #getLevel})
	 *
	 * @public
	 *
	 * @see #getDefaultLogLevel
	 * @see mmir.tools.Logger#getLevel
	 */
	setDefaultLogLevel: function(theLevel){

		if(typeof theLevel !== 'number'){
			theLevel = getAsLevel(theLevel);
		}

		_level = theLevel;
	},
	/**
	 * Sets the default log-level.
	 *
	 * @returns {Number}
	 * 			the log level: a number between 0 (verbose) and 6 (disabled)
	 *
	 * @public
	 * @see #setDefaultLogLevel
	 * @see mmir.tools.Logger#getLevel
	 */
	getDefaultLogLevel: function(){
		return _level;
	},
	/**
	 * Print log output with default logger.
	 * @public
	 * @see mmir.tools.Logger#log
	 */
	log: function(){
		_defaultLogger.log.apply(_defaultLogger, applyReverseCallStackWith(arguments, 1));
	},
	/**
	 * Print verbose output with default logger.
	 * @public
	 * @see mmir.tools.Logger#verbose
	 */
	verbose: function(){
		_defaultLogger.verbose.apply(_defaultLogger, applyReverseCallStackWith(arguments, 1));
	},
	/**
	 * Print debug message with default logger.
	 * @public
	 * @see mmir.tools.Logger#debug
	 */
	debug: function(){
		_defaultLogger.debug.apply(_defaultLogger, applyReverseCallStackWith(arguments, 1));
	},
	/**
	 * Print information message with default logger.
	 * @public
	 * @see mmir.tools.Logger#info
	 */
	info: function(){
		_defaultLogger.info.apply(_defaultLogger, applyReverseCallStackWith(arguments, 1));
	},
	/**
	 * Print warning message with default logger.
	 * @public
	 * @see mmir.tools.Logger#warn
	 */
	warn: function(){
		_defaultLogger.warn.apply(_defaultLogger, applyReverseCallStackWith(arguments, 1));
	},
	/**
	 * Print error with default logger.
	 * @public
	 * @see mmir.tools.Logger#error
	 */
	error: function(){
		_defaultLogger.error.apply(_defaultLogger, applyReverseCallStackWith(arguments, 1));
	}//,
	// isDebug : function(loggerName){
	// 	return _level <= getAsLevel('debug');
	// },
	// isInfo : function(loggerName){
	// 	return _level <= getAsLevel('info');
	// },
	// isWarn : function(loggerName){
	// 	return _level <= getAsLevel('warn');
	// },
	// isError : function(loggerName){
	// 	return _level <= getAsLevel('error');
	// }
};

//define alias'
instance.get = instance.create;


return instance;

});