logger.js 10.0 KB

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