Source: services/logger.js

'use strict';

/**
 * @author Marco Lehmann <marco.lehmann@kiwigrid.com>
 * @copyright Kiwigrid GmbH 2014-2015
 * @module keta.services.Logger
 * @description
 * <p>
 *   Logger Decorator
 * </p>
 * <p>
 *   A bitmask is used to define the <code>logLevel</code> of the whole application
 *   (if <code>'common.logLevel'</code> is set in <code>AppContext</code>). The numeric <code>logLevel</code>
 *   constants are defined on the <code>$log</code> service as <code>$log.LOG_LEVEL_LOG</code>,
 *   <code>$log.LOG_LEVEL_DEBUG</code> and so on.
 * </p>
 * <p>
 *   Because a bitwise & is used to determine the <code>logLevel</code> you have to specify all levels you want to
 *   have enabled using a bitwise | operator. You get the same logLevel by adding up the numeric value of all
 *   wanted levels and subtracting 1 from the result.
 * </p>
 * <p>
 *   The decorator also provides the new logging methods <code>$log.request(logMessage)</code> and
 *   <code>$log.event(logMessage)</code> which are described inside of <code>LoggerDecorator</code>.
 *   This section also provides information about the usage of <code>$log.ADVANCED_FORMATTER</code>.
 * </p>
  * @example
 * // enable levels 'log', 'debug', 'info'
 * "logLevel": $log.LOG_LEVEL_LOG | $log.LOG_LEVEL_DEBUG | $log.LOG_LEVEL_INFO
 * // same logLevel in numeric notation ($log.LOG_LEVEL_LOG + $log.LOG_LEVEL_DEBUG + $log.LOG_LEVEL_INFO)
 * "logLevel": 7
 */

angular.module('keta.services.Logger',
	[
		'keta.services.AppContext'
	])

	/**
	 * @class LoggerConfig
	 * @propertyOf keta.services.Logger
	 * @description Logger Config
	 */
	.config(function LoggerConfig($provide, ketaAppContextProvider) {

		/**
		 * @class LoggerDecorator
		 * @propertyOf LoggerConfig
		 * @description Logger Decorator
		 * @param {Object} $delegate delegated implementation
		 */
		$provide.decorator('$log', function LoggerDecorator($delegate) {

			// logLevel constants

			/**
			 * @name LOG_LEVEL_LOG
			 * @constant {number}
			 * @description
			 * <p>
			 *   Usage of $log.log is enabled.
			 * </p>
			 */
			$delegate.LOG_LEVEL_LOG = 1;

			/**
			 * @name LOG_LEVEL_DEBUG
			 * @constant {number}
			 * @description
			 * <p>
			 *   Usage of $log.debug is enabled.
			 * </p>
			 */
			$delegate.LOG_LEVEL_DEBUG = 2;

			/**
			 * @name LOG_LEVEL_INFO
			 * @constant {number}
			 * @description
			 * <p>
			 *   Usage of $log.info is enabled.
			 * </p>
			 */
			$delegate.LOG_LEVEL_INFO = 4;

			/**
			 * @name LOG_LEVEL_WARN
			 * @constant {number}
			 * @description
			 * <p>
			 *   Usage of $log.warn is enabled.
			 * </p>
			 */
			$delegate.LOG_LEVEL_WARN = 8;

			/**
			 * @name LOG_LEVEL_ERROR
			 * @constant {number}
			 * @description
			 * <p>
			 *   Usage of $log.error is enabled.
			 * </p>
			 */
			$delegate.LOG_LEVEL_ERROR = 16;

			// decorate given logging method
			var decorateLogger = function(originalFn, bitValue) {
				return function() {
					var args = Array.prototype.slice.call(arguments);

					// check logLevel and adjust console output accordingly through bitmask
					if (ketaAppContextProvider.get('common.logLevel') === null ||
						ketaAppContextProvider.get('common.logLevel') === 0 ||
						(ketaAppContextProvider.get('common.logLevel') & bitValue) === bitValue) {
						originalFn.apply(null, args);
					}
				};
			};

			// decorate all the common logging methods
			angular.forEach(['log', 'debug', 'info', 'warn', 'error'], function(o) {
				var bitValue = $delegate['LOG_LEVEL_' + o.toUpperCase()];
				$delegate[o] = decorateLogger($delegate[o], bitValue);
				// this keeps angular-mocks happy
				$delegate[o].logs = [];
			});

			/**
			 * @name ADVANCED_FORMATTER
			 * @function
			 * @description
			 * <p>
			 *   Formats a message in an advanced, colored manner.
			 * </p>
			 * @param {Array} messages Messages as array
			 * @returns {void} returns nothing
			 * @example
			 * angular.module('exampleApp', ['keta.services.Logger'])
			 *     .controller('ExampleController', function($log) {
			 *         $log.request([request, response], $log.ADVANCED_FORMATTER);
			 *     });
			 */
			$delegate.ADVANCED_FORMATTER = function(messages) {

				if (!angular.isArray(messages)) {
					messages = [messages];
				}

				var output = '%c[' + new Date().toISOString() + ']\n%c';
				angular.forEach(messages, function(message) {
					output += JSON.stringify(message, null, '\t') + '\n';
				});

				$delegate.log(
					output,
					'color:#acbf2f;font-weight:bold;',
					'color:#333;font-weight:normal;'
				);

			};

			/**
			 * @name request
			 * @function
			 * @description
			 * <p>
			 *   Logs a message-based request using <code>console.log</code>. Additionally a custom or
			 *   predefined formatter (<code>ADVANCED_FORMATTER</code>) can be specified.
			 * </p>
			 * @param {Array} messages Messages to log
			 * @param {function} [formatter] Formatter to use
			 * @returns {void} returns nothing
			 * @example
			 * angular.module('exampleApp', ['keta.services.Logger'])
			 *     .controller('ExampleController', function($log) {
			 *
			 *         // use no formatter
			 *         $log.request([request, response]);
			 *
			 *         // use ADVANCED_FORMATTER
			 *         $log.request([request, response], $log.ADVANCED_FORMATTER);
			 *
			 *         // use custom formatter
			 *         $log.request([request, response], function(messages) {
			 *             // custom logging
			 *         });
			 *
			 *     });
			 */
			$delegate.request = function(messages, formatter) {
				if (angular.isDefined(formatter) && angular.isFunction(formatter)) {
					formatter(messages);
				} else {
					$delegate.log(messages);
				}
			};

			/**
			 * @name event
			 * @function
			 * @description
			 * <p>
			 *   Logs a message-based event using <code>console.log</code>. Additionally a custom or
			 *   predefined formatter (<code>ADVANCED_FORMATTER</code>) can be specified.
			 * </p>
			 * @param {Array} messages Messages to log
			 * @param {function} [formatter] Formatter to use
			 * @returns {void} returns nothing
			 * @example
			 * angular.module('exampleApp', ['keta.services.Logger'])
			 *     .controller('ExampleController', function($log) {
			 *
			 *         // use no formatter
			 *         $log.event(event);
			 *
			 *         // use ADVANCED_FORMATTER
			 *         $log.event(event, $log.ADVANCED_FORMATTER);
			 *
			 *         // use custom formatter
			 *         $log.event(event, function(messages) {
			 *             // custom logging
			 *         });
			 *
			 *     });
			 */
			$delegate.event = function(messages, formatter) {
				$delegate.request(messages, formatter);
			};

			return $delegate;

		});

	});