/* * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * */ // ------------------------------------------------------------------------------ // The logger module exports the following properties/functions: // // LOG - constant for the level LOG // ERROR - constant for the level ERROR // WARN - constant for the level WARN // INFO - constant for the level INFO // DEBUG - constant for the level DEBUG // logLevel() - returns current log level // logLevel(value) - sets and returns a new log level // useConsole() - returns whether logger is using console // useConsole(value) - sets and returns whether logger is using console // log(message,...) - logs a message at level LOG // error(message,...) - logs a message at level ERROR // warn(message,...) - logs a message at level WARN // info(message,...) - logs a message at level INFO // debug(message,...) - logs a message at level DEBUG // logLevel(level,message,...) - logs a message specified level // // ------------------------------------------------------------------------------ var logger = exports; var exec = require('cordova/exec'); var UseConsole = false; var UseLogger = true; var Queued = []; var DeviceReady = false; var CurrentLevel; var originalConsole = console; /** * Logging levels */ var Levels = [ 'LOG', 'ERROR', 'WARN', 'INFO', 'DEBUG' ]; /* * add the logging levels to the logger object and * to a separate levelsMap object for testing */ var LevelsMap = {}; for (var i = 0; i < Levels.length; i++) { var level = Levels[i]; LevelsMap[level] = i; logger[level] = level; } CurrentLevel = LevelsMap.WARN; /** * Getter/Setter for the logging level * * Returns the current logging level. * * When a value is passed, sets the logging level to that value. * The values should be one of the following constants: * logger.LOG * logger.ERROR * logger.WARN * logger.INFO * logger.DEBUG * * The value used determines which messages get printed. The logging * values above are in order, and only messages logged at the logging * level or above will actually be displayed to the user. E.g., the * default level is WARN, so only messages logged with LOG, ERROR, or * WARN will be displayed; INFO and DEBUG messages will be ignored. */ logger.level = function (value) { if (arguments.length) { if (LevelsMap[value] === null) { throw new Error('invalid logging level: ' + value); } CurrentLevel = LevelsMap[value]; } return Levels[CurrentLevel]; }; /** * Getter/Setter for the useConsole functionality * * When useConsole is true, the logger will log via the * browser 'console' object. */ logger.useConsole = function (value) { if (arguments.length) UseConsole = !!value; if (UseConsole) { if (typeof console === 'undefined') { throw new Error('global console object is not defined'); } if (typeof console.log !== 'function') { throw new Error('global console object does not have a log function'); } if (typeof console.useLogger === 'function') { if (console.useLogger()) { throw new Error('console and logger are too intertwingly'); } } } return UseConsole; }; /** * Getter/Setter for the useLogger functionality * * When useLogger is true, the logger will log via the * native Logger plugin. */ logger.useLogger = function (value) { // Enforce boolean if (arguments.length) UseLogger = !!value; return UseLogger; }; /** * Logs a message at the LOG level. * * Parameters passed after message are used applied to * the message with utils.format() */ logger.log = function (message) { logWithArgs('LOG', arguments); }; /** * Logs a message at the ERROR level. * * Parameters passed after message are used applied to * the message with utils.format() */ logger.error = function (message) { logWithArgs('ERROR', arguments); }; /** * Logs a message at the WARN level. * * Parameters passed after message are used applied to * the message with utils.format() */ logger.warn = function (message) { logWithArgs('WARN', arguments); }; /** * Logs a message at the INFO level. * * Parameters passed after message are used applied to * the message with utils.format() */ logger.info = function (message) { logWithArgs('INFO', arguments); }; /** * Logs a message at the DEBUG level. * * Parameters passed after message are used applied to * the message with utils.format() */ logger.debug = function (message) { logWithArgs('DEBUG', arguments); }; // log at the specified level with args function logWithArgs (level, args) { args = [level].concat([].slice.call(args)); logger.logLevel.apply(logger, args); } // return the correct formatString for an object function formatStringForMessage (message) { return (typeof message === 'string') ? '' : '%o'; } /** * Logs a message at the specified level. * * Parameters passed after message are used applied to * the message with utils.format() */ logger.logLevel = function (level /* , ... */) { // format the message with the parameters var formatArgs = [].slice.call(arguments, 1); var fmtString = formatStringForMessage(formatArgs[0]); if (fmtString.length > 0) { formatArgs.unshift(fmtString); // add formatString } var message = logger.format.apply(logger.format, formatArgs); if (LevelsMap[level] === null) { throw new Error('invalid logging level: ' + level); } if (LevelsMap[level] > CurrentLevel) return; // queue the message if not yet at deviceready if (!DeviceReady && !UseConsole) { Queued.push([level, message]); return; } // Log using the native logger if that is enabled if (UseLogger) { exec(null, null, 'Console', 'logLevel', [level, message]); } // Log using the console if that is enabled if (UseConsole) { // make sure console is not using logger if (console.useLogger()) { throw new Error('console and logger are too intertwingly'); } // log to the console switch (level) { case logger.LOG: originalConsole.log(message); break; case logger.ERROR: originalConsole.log('ERROR: ' + message); break; case logger.WARN: originalConsole.log('WARN: ' + message); break; case logger.INFO: originalConsole.log('INFO: ' + message); break; case logger.DEBUG: originalConsole.log('DEBUG: ' + message); break; } } }; /** * Formats a string and arguments following it ala console.log() * * Any remaining arguments will be appended to the formatted string. * * for rationale, see FireBug's Console API: * http://getfirebug.com/wiki/index.php/Console_API */ logger.format = function (formatString, args) { return __format(arguments[0], [].slice.call(arguments, 1)).join(' '); }; // ------------------------------------------------------------------------------ /** * Formats a string and arguments following it ala vsprintf() * * format chars: * %j - format arg as JSON * %o - format arg as JSON * %c - format arg as '' * %% - replace with '%' * any other char following % will format it's * arg via toString(). * * Returns an array containing the formatted string and any remaining * arguments. */ function __format (formatString, args) { if (formatString === null || formatString === undefined) return ['']; if (arguments.length === 1) return [formatString.toString()]; if (typeof formatString !== 'string') { formatString = formatString.toString(); } var pattern = /(.*?)%(.)(.*)/; var rest = formatString; var result = []; while (args.length) { var match = pattern.exec(rest); if (!match) break; var arg = args.shift(); rest = match[3]; result.push(match[1]); if (match[2] === '%') { result.push('%'); args.unshift(arg); continue; } result.push(__formatted(arg, match[2])); } result.push(rest); var remainingArgs = [].slice.call(args); remainingArgs.unshift(result.join('')); return remainingArgs; } function __formatted (object, formatChar) { try { switch (formatChar) { case 'j': case 'o': return JSON.stringify(object); case 'c': return ''; } } catch (e) { return 'error JSON.stringify()ing argument: ' + e; } if ((object === null) || (object === undefined)) { return Object.prototype.toString.call(object); } return object.toString(); } // ------------------------------------------------------------------------------ // when deviceready fires, log queued messages logger.__onDeviceReady = function () { if (DeviceReady) return; DeviceReady = true; for (var i = 0; i < Queued.length; i++) { var messageArgs = Queued[i]; logger.logLevel(messageArgs[0], messageArgs[1]); } Queued = null; }; // add a deviceready event to log queued messages document.addEventListener('deviceready', logger.__onDeviceReady, false);