Home Reference Source Repository

js/base/services/LoggerService.js

'use strict';
/*
 * base/js/services/services.LoggerService.js
 */
var LoggerService = ($q, $log, $injector, SynthConfig) => {
// Map of loggers created
	const loggers = {};

	// Contants for log levels
	const LEVELS = {
		'DEBUG' : 1,
		'INFO' : 2,
		'WARN' : 3,
		'ERROR' : 4,
		'NONE' : 5
	};

	// Set the current log level from the SynthConfig
	var logLevel = SynthConfig.logLevel;

	// Should we log to console
	var logToConsole = SynthConfig.logToConsole;

	// Should we log to a file
	var logToFile = SynthConfig.logToFile;

	// Number of log files to keep
	var numFiles = SynthConfig.logFileCount;

	/**
	 * TODO remove this, device will be ready before angular starts
	 */
	function cordovaReady(){
		return $q.when();
	}

	/**
	 * Get the directory entry where the logs are stored
	 */
	function getLogsDirectory(){
		return cordovaReady()
			.then(function(){
				var defer = $q.defer();
				window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);

				function gotFS(fileSystem) {
					fileSystem.root.getDirectory(SynthConfig.dataDir, {create : true, exclusive : false},
						function(directory){
							defer.resolve(directory);
						}, fail
					);
				}
				function fail(error) {
					$log.warn('LoggerService: Failed to get filesystem, code:' + error.code);
					defer.reject(error);
				}
				return defer.promise;
			})
			// Ensure log directory exists
			.then(function(rootDirectoryEntry){
				var defer = $q.defer();
				rootDirectoryEntry.getDirectory('logs', {create : true}, function(dirEntry) {
					defer.resolve(dirEntry);
				});
				return defer.promise;
			});
	}

	/**
	 * Gets a log entry for the specified logfile name
	 */
	function getLogFileEntry(logFileName){
		return getLogsDirectory()
			// Get the log file
			.then(function(logDirEntry){
				var defer = $q.defer();
				logDirEntry.getFile(logFileName, {create : true},
					function(fileEntry){
						defer.resolve(fileEntry);
					},
					function(error){
						defer.reject(error);
					}
				);
				return defer.promise;
			});
	}

	/**
	 * Removes the last log file
	 */
	function removeFileLastLog(){
		const defer = $q.defer();
		getLogFileEntry('log.' + numFiles + '.txt')
			.then(function(fileEntry){
				fileEntry.remove(function(){
					defer.resolve();
				},
					function(error){
						defer.reject(error);
					});
			});
		return defer.promise;
	}

	function shiftLog(idx){
		const defer = $q.defer();

		function fail(error){
			defer.reject(error);
		}

		getLogFileEntry('log.' + idx + '.txt')
			.then(function(fileEntry){
				fileEntry.getParent(function(parentEntry){
					// move the directory to a new directory and rename it
					fileEntry.moveTo(parentEntry, 'log.' + (idx + 1) + '.txt', function(newEntry){
						defer.resolve(newEntry);
					}, fail);
				}, fail);
			});
		return defer.promise;
	}

	/**
	 * Rotate the log files
	 */
	function rotateLogs(){
		function getShiftLogPromise(){
			/*eslint no-loop-func: "off"*/
			let promise = $q.when();
			for(var idx = numFiles - 1; idx >= 0; idx--){
				promise = promise.then(function(){
					return shiftLog(idx);
				});
			}
			return promise;
		}

		function getNewLogPromise(){
			return getLogFileEntry('log.0.txt');
		}

		// Delete last log file
		return removeFileLastLog()
			// Shift remaining files
			.then(getShiftLogPromise)
			.then(getNewLogPromise);
	}

	/**
	 * Get the log file entry
	 */
	function initService(){
		rotateLogs().then((newFileEntry) => {
			return newFileEntry;
		});
	}

	function writeToFile(logString){
		getLogFileEntry('log.0.txt').then((logFileEntry) => {
			var defer = $q.defer();
			logFileEntry.createWriter(onWriterReady, fail);

			// Writer is ready to write
			function onWriterReady(writer) {
				writer.onwriteend = function() {
					defer.resolve();
				};
				// Seek to the endo of the file
				writer.seek(writer.length);
				writer.write(logString + '\n');
			}
			// Function for when an IO action fails
			function fail(error) {
				$log.warn('Failed while trying to write to file, error: ' + error);
				defer.reject(error);
			}
			return defer.promise;
		});
	}

	/**
	 * Constructor
	 */
	class LoggerServiceImpl{

		constructor(name){
			this.name = name;
			this.levels = LEVELS;
		}

		debug(message){
			if (this.isDEBUG()){
				var logString = moment().format('YYYY-MM-DD HH:mm:ss') + ' ' + this.name + ' (DEBUG) : ' + message;
				/*eslint-disable*/
				logToConsole && $log.log(logString);
				logToFile && writeToFile(logString);
				/*eslint-enable*/
			}
		}

		warn(message){
			if (this.isWARN()){
				var logString = moment().format('YYYY-MM-DD HH:mm:ss') + ' ' + this.name + ' (WARN) : ' + message;
				/*eslint-disable*/
				logToConsole && $log.warn(logString);
				logToFile && writeToFile(logString);
				/*eslint-enable*/
			}
		}

		info(message){
			if (this.isINFO()){
				var logString = moment().format('YYYY-MM-DD HH:mm:ss') + ' ' + this.name + ' (INFO) : ' + message;
				/*eslint-disable*/
				logToConsole && $log.info(logString);
				logToFile && writeToFile(logString);
				/*eslint-enable*/
			}
		}

		error(message){
			if (this.isERROR()){
				var logString = moment().format('YYYY-MM-DD HH:mm:ss') + ' ' + this.name + ' (ERROR) : ' + message;
				/*eslint-disable*/
				logToConsole && $log.error(logString);
				logToFile && writeToFile(logString);
				/*eslint-enable*/
			}
		}

		/**
		 * Returns true if ERROR level should be logged
		 */
		isERROR(){
			return logLevel <= LEVELS.ERROR;
		}

		/**
		 * Returns true if INFO should be logged
		 */
		isINFO(){
			return logLevel <= LEVELS.INFO;
		}

		/**
		 * Returns true if WARN should be logged
		 */
		isWARN(){
			return logLevel <= LEVELS.WARN;
		}

		/**
		 * Returns true if DEBUG should be logged
		 */
		isDEBUG(){
			return logLevel <= LEVELS.DEBUG;
		}

	}

	var systemLogger = new LoggerServiceImpl('SYSTEM');

	// Replace console.log with system logger
	/*eslint-disable*/
	window.console.log 				= function(message){systemLogger.debug(message);};
	window.console.warn 			= function(message){systemLogger.warn(message);};
	window.console.error			= function(message){systemLogger.error(message);};
	window.console.info				= function(message){systemLogger.info(message);};
	/*eslint-enable*/
	window.console.exception		= window.console.log;
	window.console.trace 			= window.console.log;
	window.console.group 			= window.console.log;
	window.console.groupCollapsed	= window.console.log;

	//------------------------------------------------------------------------------
	window.console.table = function(data) {
		console.log('%o', data);
	};

	// Initialise this service by rotating previous logs
	initService();
	return function(name){
		if (loggers[name] === undefined){
			loggers[name] = new LoggerServiceImpl(name);
		}
		return loggers[name];
	};

};
LoggerService.$inject = ['$q', '$log', '$injector', 'SynthConfig'];
export default LoggerService;