logger.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. /*
  2. *
  3. * Licensed to the Apache Software Foundation (ASF) under one
  4. * or more contributor license agreements. See the NOTICE file
  5. * distributed with this work for additional information
  6. * regarding copyright ownership. The ASF licenses this file
  7. * to you under the Apache License, Version 2.0 (the
  8. * "License"); you may not use this file except in compliance
  9. * with the License. You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing,
  14. * software distributed under the License is distributed on an
  15. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  16. * KIND, either express or implied. See the License for the
  17. * specific language governing permissions and limitations
  18. * under the License.
  19. *
  20. */
  21. //------------------------------------------------------------------------------
  22. // The logger module exports the following properties/functions:
  23. //
  24. // LOG - constant for the level LOG
  25. // ERROR - constant for the level ERROR
  26. // WARN - constant for the level WARN
  27. // INFO - constant for the level INFO
  28. // DEBUG - constant for the level DEBUG
  29. // logLevel() - returns current log level
  30. // logLevel(value) - sets and returns a new log level
  31. // useConsole() - returns whether logger is using console
  32. // useConsole(value) - sets and returns whether logger is using console
  33. // log(message,...) - logs a message at level LOG
  34. // error(message,...) - logs a message at level ERROR
  35. // warn(message,...) - logs a message at level WARN
  36. // info(message,...) - logs a message at level INFO
  37. // debug(message,...) - logs a message at level DEBUG
  38. // logLevel(level,message,...) - logs a message specified level
  39. //
  40. //------------------------------------------------------------------------------
  41. var logger = exports;
  42. var exec = require('cordova/exec');
  43. var utils = require('cordova/utils');
  44. var UseConsole = false;
  45. var UseLogger = true;
  46. var Queued = [];
  47. var DeviceReady = false;
  48. var CurrentLevel;
  49. var originalConsole = console;
  50. /**
  51. * Logging levels
  52. */
  53. var Levels = [
  54. "LOG",
  55. "ERROR",
  56. "WARN",
  57. "INFO",
  58. "DEBUG"
  59. ];
  60. /*
  61. * add the logging levels to the logger object and
  62. * to a separate levelsMap object for testing
  63. */
  64. var LevelsMap = {};
  65. for (var i=0; i<Levels.length; i++) {
  66. var level = Levels[i];
  67. LevelsMap[level] = i;
  68. logger[level] = level;
  69. }
  70. CurrentLevel = LevelsMap.WARN;
  71. /**
  72. * Getter/Setter for the logging level
  73. *
  74. * Returns the current logging level.
  75. *
  76. * When a value is passed, sets the logging level to that value.
  77. * The values should be one of the following constants:
  78. * logger.LOG
  79. * logger.ERROR
  80. * logger.WARN
  81. * logger.INFO
  82. * logger.DEBUG
  83. *
  84. * The value used determines which messages get printed. The logging
  85. * values above are in order, and only messages logged at the logging
  86. * level or above will actually be displayed to the user. E.g., the
  87. * default level is WARN, so only messages logged with LOG, ERROR, or
  88. * WARN will be displayed; INFO and DEBUG messages will be ignored.
  89. */
  90. logger.level = function (value) {
  91. if (arguments.length) {
  92. if (LevelsMap[value] === null) {
  93. throw new Error("invalid logging level: " + value);
  94. }
  95. CurrentLevel = LevelsMap[value];
  96. }
  97. return Levels[CurrentLevel];
  98. };
  99. /**
  100. * Getter/Setter for the useConsole functionality
  101. *
  102. * When useConsole is true, the logger will log via the
  103. * browser 'console' object.
  104. */
  105. logger.useConsole = function (value) {
  106. if (arguments.length) UseConsole = !!value;
  107. if (UseConsole) {
  108. if (typeof console == "undefined") {
  109. throw new Error("global console object is not defined");
  110. }
  111. if (typeof console.log != "function") {
  112. throw new Error("global console object does not have a log function");
  113. }
  114. if (typeof console.useLogger == "function") {
  115. if (console.useLogger()) {
  116. throw new Error("console and logger are too intertwingly");
  117. }
  118. }
  119. }
  120. return UseConsole;
  121. };
  122. /**
  123. * Getter/Setter for the useLogger functionality
  124. *
  125. * When useLogger is true, the logger will log via the
  126. * native Logger plugin.
  127. */
  128. logger.useLogger = function (value) {
  129. // Enforce boolean
  130. if (arguments.length) UseLogger = !!value;
  131. return UseLogger;
  132. };
  133. /**
  134. * Logs a message at the LOG level.
  135. *
  136. * Parameters passed after message are used applied to
  137. * the message with utils.format()
  138. */
  139. logger.log = function(message) { logWithArgs("LOG", arguments); };
  140. /**
  141. * Logs a message at the ERROR level.
  142. *
  143. * Parameters passed after message are used applied to
  144. * the message with utils.format()
  145. */
  146. logger.error = function(message) { logWithArgs("ERROR", arguments); };
  147. /**
  148. * Logs a message at the WARN level.
  149. *
  150. * Parameters passed after message are used applied to
  151. * the message with utils.format()
  152. */
  153. logger.warn = function(message) { logWithArgs("WARN", arguments); };
  154. /**
  155. * Logs a message at the INFO level.
  156. *
  157. * Parameters passed after message are used applied to
  158. * the message with utils.format()
  159. */
  160. logger.info = function(message) { logWithArgs("INFO", arguments); };
  161. /**
  162. * Logs a message at the DEBUG level.
  163. *
  164. * Parameters passed after message are used applied to
  165. * the message with utils.format()
  166. */
  167. logger.debug = function(message) { logWithArgs("DEBUG", arguments); };
  168. // log at the specified level with args
  169. function logWithArgs(level, args) {
  170. args = [level].concat([].slice.call(args));
  171. logger.logLevel.apply(logger, args);
  172. }
  173. // return the correct formatString for an object
  174. function formatStringForMessage(message) {
  175. return (typeof message === "string") ? "" : "%o";
  176. }
  177. /**
  178. * Logs a message at the specified level.
  179. *
  180. * Parameters passed after message are used applied to
  181. * the message with utils.format()
  182. */
  183. logger.logLevel = function(level /* , ... */) {
  184. // format the message with the parameters
  185. var formatArgs = [].slice.call(arguments, 1);
  186. var fmtString = formatStringForMessage(formatArgs[0]);
  187. if (fmtString.length > 0){
  188. formatArgs.unshift(fmtString); // add formatString
  189. }
  190. var message = logger.format.apply(logger.format, formatArgs);
  191. if (LevelsMap[level] === null) {
  192. throw new Error("invalid logging level: " + level);
  193. }
  194. if (LevelsMap[level] > CurrentLevel) return;
  195. // queue the message if not yet at deviceready
  196. if (!DeviceReady && !UseConsole) {
  197. Queued.push([level, message]);
  198. return;
  199. }
  200. // Log using the native logger if that is enabled
  201. if (UseLogger) {
  202. exec(null, null, "Console", "logLevel", [level, message]);
  203. }
  204. // Log using the console if that is enabled
  205. if (UseConsole) {
  206. // make sure console is not using logger
  207. if (console.useLogger()) {
  208. throw new Error("console and logger are too intertwingly");
  209. }
  210. // log to the console
  211. switch (level) {
  212. case logger.LOG: originalConsole.log(message); break;
  213. case logger.ERROR: originalConsole.log("ERROR: " + message); break;
  214. case logger.WARN: originalConsole.log("WARN: " + message); break;
  215. case logger.INFO: originalConsole.log("INFO: " + message); break;
  216. case logger.DEBUG: originalConsole.log("DEBUG: " + message); break;
  217. }
  218. }
  219. };
  220. /**
  221. * Formats a string and arguments following it ala console.log()
  222. *
  223. * Any remaining arguments will be appended to the formatted string.
  224. *
  225. * for rationale, see FireBug's Console API:
  226. * http://getfirebug.com/wiki/index.php/Console_API
  227. */
  228. logger.format = function(formatString, args) {
  229. return __format(arguments[0], [].slice.call(arguments,1)).join(' ');
  230. };
  231. //------------------------------------------------------------------------------
  232. /**
  233. * Formats a string and arguments following it ala vsprintf()
  234. *
  235. * format chars:
  236. * %j - format arg as JSON
  237. * %o - format arg as JSON
  238. * %c - format arg as ''
  239. * %% - replace with '%'
  240. * any other char following % will format it's
  241. * arg via toString().
  242. *
  243. * Returns an array containing the formatted string and any remaining
  244. * arguments.
  245. */
  246. function __format(formatString, args) {
  247. if (formatString === null || formatString === undefined) return [""];
  248. if (arguments.length == 1) return [formatString.toString()];
  249. if (typeof formatString != "string")
  250. formatString = formatString.toString();
  251. var pattern = /(.*?)%(.)(.*)/;
  252. var rest = formatString;
  253. var result = [];
  254. while (args.length) {
  255. var match = pattern.exec(rest);
  256. if (!match) break;
  257. var arg = args.shift();
  258. rest = match[3];
  259. result.push(match[1]);
  260. if (match[2] == '%') {
  261. result.push('%');
  262. args.unshift(arg);
  263. continue;
  264. }
  265. result.push(__formatted(arg, match[2]));
  266. }
  267. result.push(rest);
  268. var remainingArgs = [].slice.call(args);
  269. remainingArgs.unshift(result.join(''));
  270. return remainingArgs;
  271. }
  272. function __formatted(object, formatChar) {
  273. try {
  274. switch(formatChar) {
  275. case 'j':
  276. case 'o': return JSON.stringify(object);
  277. case 'c': return '';
  278. }
  279. }
  280. catch (e) {
  281. return "error JSON.stringify()ing argument: " + e;
  282. }
  283. if ((object === null) || (object === undefined)) {
  284. return Object.prototype.toString.call(object);
  285. }
  286. return object.toString();
  287. }
  288. //------------------------------------------------------------------------------
  289. // when deviceready fires, log queued messages
  290. logger.__onDeviceReady = function() {
  291. if (DeviceReady) return;
  292. DeviceReady = true;
  293. for (var i=0; i<Queued.length; i++) {
  294. var messageArgs = Queued[i];
  295. logger.logLevel(messageArgs[0], messageArgs[1]);
  296. }
  297. Queued = null;
  298. };
  299. // add a deviceready event to log queued messages
  300. document.addEventListener("deviceready", logger.__onDeviceReady, false);