loglevel.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. /*
  2. * loglevel - https://github.com/pimterry/loglevel
  3. *
  4. * Copyright (c) 2013 Tim Perry
  5. * Licensed under the MIT license.
  6. */
  7. (function (root, definition) {
  8. "use strict";
  9. if (typeof define === 'function' && define.amd) {
  10. define(definition);
  11. } else if (typeof module === 'object' && module.exports) {
  12. module.exports = definition();
  13. } else {
  14. root.log = definition();
  15. }
  16. }(this, function () {
  17. "use strict";
  18. // Slightly dubious tricks to cut down minimized file size
  19. var noop = function() {};
  20. var undefinedType = "undefined";
  21. var isIE = (typeof window !== undefinedType) && (typeof window.navigator !== undefinedType) && (
  22. /Trident\/|MSIE /.test(window.navigator.userAgent)
  23. );
  24. var logMethods = [
  25. "trace",
  26. "debug",
  27. "info",
  28. "warn",
  29. "error"
  30. ];
  31. var _loggersByName = {};
  32. var defaultLogger = null;
  33. // Cross-browser bind equivalent that works at least back to IE6
  34. function bindMethod(obj, methodName) {
  35. var method = obj[methodName];
  36. if (typeof method.bind === 'function') {
  37. return method.bind(obj);
  38. } else {
  39. try {
  40. return Function.prototype.bind.call(method, obj);
  41. } catch (e) {
  42. // Missing bind shim or IE8 + Modernizr, fallback to wrapping
  43. return function() {
  44. return Function.prototype.apply.apply(method, [obj, arguments]);
  45. };
  46. }
  47. }
  48. }
  49. // Trace() doesn't print the message in IE, so for that case we need to wrap it
  50. function traceForIE() {
  51. if (console.log) {
  52. if (console.log.apply) {
  53. console.log.apply(console, arguments);
  54. } else {
  55. // In old IE, native console methods themselves don't have apply().
  56. Function.prototype.apply.apply(console.log, [console, arguments]);
  57. }
  58. }
  59. if (console.trace) console.trace();
  60. }
  61. // Build the best logging method possible for this env
  62. // Wherever possible we want to bind, not wrap, to preserve stack traces
  63. function realMethod(methodName) {
  64. if (methodName === 'debug') {
  65. methodName = 'log';
  66. }
  67. if (typeof console === undefinedType) {
  68. return false; // No method possible, for now - fixed later by enableLoggingWhenConsoleArrives
  69. } else if (methodName === 'trace' && isIE) {
  70. return traceForIE;
  71. } else if (console[methodName] !== undefined) {
  72. return bindMethod(console, methodName);
  73. } else if (console.log !== undefined) {
  74. return bindMethod(console, 'log');
  75. } else {
  76. return noop;
  77. }
  78. }
  79. // These private functions always need `this` to be set properly
  80. function replaceLoggingMethods() {
  81. /*jshint validthis:true */
  82. var level = this.getLevel();
  83. // Replace the actual methods.
  84. for (var i = 0; i < logMethods.length; i++) {
  85. var methodName = logMethods[i];
  86. this[methodName] = (i < level) ?
  87. noop :
  88. this.methodFactory(methodName, level, this.name);
  89. }
  90. // Define log.log as an alias for log.debug
  91. this.log = this.debug;
  92. // Return any important warnings.
  93. if (typeof console === undefinedType && level < this.levels.SILENT) {
  94. return "No console available for logging";
  95. }
  96. }
  97. // In old IE versions, the console isn't present until you first open it.
  98. // We build realMethod() replacements here that regenerate logging methods
  99. function enableLoggingWhenConsoleArrives(methodName) {
  100. return function () {
  101. if (typeof console !== undefinedType) {
  102. replaceLoggingMethods.call(this);
  103. this[methodName].apply(this, arguments);
  104. }
  105. };
  106. }
  107. // By default, we use closely bound real methods wherever possible, and
  108. // otherwise we wait for a console to appear, and then try again.
  109. function defaultMethodFactory(methodName, _level, _loggerName) {
  110. /*jshint validthis:true */
  111. return realMethod(methodName) ||
  112. enableLoggingWhenConsoleArrives.apply(this, arguments);
  113. }
  114. function Logger(name, factory) {
  115. // Private instance variables.
  116. var self = this;
  117. /**
  118. * The level inherited from a parent logger (or a global default). We
  119. * cache this here rather than delegating to the parent so that it stays
  120. * in sync with the actual logging methods that we have installed (the
  121. * parent could change levels but we might not have rebuilt the loggers
  122. * in this child yet).
  123. * @type {number}
  124. */
  125. var inheritedLevel;
  126. /**
  127. * The default level for this logger, if any. If set, this overrides
  128. * `inheritedLevel`.
  129. * @type {number|null}
  130. */
  131. var defaultLevel;
  132. /**
  133. * A user-specific level for this logger. If set, this overrides
  134. * `defaultLevel`.
  135. * @type {number|null}
  136. */
  137. var userLevel;
  138. var storageKey = "loglevel";
  139. if (typeof name === "string") {
  140. storageKey += ":" + name;
  141. } else if (typeof name === "symbol") {
  142. storageKey = undefined;
  143. }
  144. function persistLevelIfPossible(levelNum) {
  145. var levelName = (logMethods[levelNum] || 'silent').toUpperCase();
  146. if (typeof window === undefinedType || !storageKey) return;
  147. // Use localStorage if available
  148. try {
  149. window.localStorage[storageKey] = levelName;
  150. return;
  151. } catch (ignore) {}
  152. // Use session cookie as fallback
  153. try {
  154. window.document.cookie =
  155. encodeURIComponent(storageKey) + "=" + levelName + ";";
  156. } catch (ignore) {}
  157. }
  158. function getPersistedLevel() {
  159. var storedLevel;
  160. if (typeof window === undefinedType || !storageKey) return;
  161. try {
  162. storedLevel = window.localStorage[storageKey];
  163. } catch (ignore) {}
  164. // Fallback to cookies if local storage gives us nothing
  165. if (typeof storedLevel === undefinedType) {
  166. try {
  167. var cookie = window.document.cookie;
  168. var cookieName = encodeURIComponent(storageKey);
  169. var location = cookie.indexOf(cookieName + "=");
  170. if (location !== -1) {
  171. storedLevel = /^([^;]+)/.exec(
  172. cookie.slice(location + cookieName.length + 1)
  173. )[1];
  174. }
  175. } catch (ignore) {}
  176. }
  177. // If the stored level is not valid, treat it as if nothing was stored.
  178. if (self.levels[storedLevel] === undefined) {
  179. storedLevel = undefined;
  180. }
  181. return storedLevel;
  182. }
  183. function clearPersistedLevel() {
  184. if (typeof window === undefinedType || !storageKey) return;
  185. // Use localStorage if available
  186. try {
  187. window.localStorage.removeItem(storageKey);
  188. } catch (ignore) {}
  189. // Use session cookie as fallback
  190. try {
  191. window.document.cookie =
  192. encodeURIComponent(storageKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC";
  193. } catch (ignore) {}
  194. }
  195. function normalizeLevel(input) {
  196. var level = input;
  197. if (typeof level === "string" && self.levels[level.toUpperCase()] !== undefined) {
  198. level = self.levels[level.toUpperCase()];
  199. }
  200. if (typeof level === "number" && level >= 0 && level <= self.levels.SILENT) {
  201. return level;
  202. } else {
  203. throw new TypeError("log.setLevel() called with invalid level: " + input);
  204. }
  205. }
  206. /*
  207. *
  208. * Public logger API - see https://github.com/pimterry/loglevel for details
  209. *
  210. */
  211. self.name = name;
  212. self.levels = { "TRACE": 0, "DEBUG": 1, "INFO": 2, "WARN": 3,
  213. "ERROR": 4, "SILENT": 5};
  214. self.methodFactory = factory || defaultMethodFactory;
  215. self.getLevel = function () {
  216. if (userLevel != null) {
  217. return userLevel;
  218. } else if (defaultLevel != null) {
  219. return defaultLevel;
  220. } else {
  221. return inheritedLevel;
  222. }
  223. };
  224. self.setLevel = function (level, persist) {
  225. userLevel = normalizeLevel(level);
  226. if (persist !== false) { // defaults to true
  227. persistLevelIfPossible(userLevel);
  228. }
  229. // NOTE: in v2, this should call rebuild(), which updates children.
  230. return replaceLoggingMethods.call(self);
  231. };
  232. self.setDefaultLevel = function (level) {
  233. defaultLevel = normalizeLevel(level);
  234. if (!getPersistedLevel()) {
  235. self.setLevel(level, false);
  236. }
  237. };
  238. self.resetLevel = function () {
  239. userLevel = null;
  240. clearPersistedLevel();
  241. replaceLoggingMethods.call(self);
  242. };
  243. self.enableAll = function(persist) {
  244. self.setLevel(self.levels.TRACE, persist);
  245. };
  246. self.disableAll = function(persist) {
  247. self.setLevel(self.levels.SILENT, persist);
  248. };
  249. self.rebuild = function () {
  250. if (defaultLogger !== self) {
  251. inheritedLevel = normalizeLevel(defaultLogger.getLevel());
  252. }
  253. replaceLoggingMethods.call(self);
  254. if (defaultLogger === self) {
  255. for (var childName in _loggersByName) {
  256. _loggersByName[childName].rebuild();
  257. }
  258. }
  259. };
  260. // Initialize all the internal levels.
  261. inheritedLevel = normalizeLevel(
  262. defaultLogger ? defaultLogger.getLevel() : "WARN"
  263. );
  264. var initialLevel = getPersistedLevel();
  265. if (initialLevel != null) {
  266. userLevel = normalizeLevel(initialLevel);
  267. }
  268. replaceLoggingMethods.call(self);
  269. }
  270. /*
  271. *
  272. * Top-level API
  273. *
  274. */
  275. defaultLogger = new Logger();
  276. defaultLogger.getLogger = function getLogger(name) {
  277. if ((typeof name !== "symbol" && typeof name !== "string") || name === "") {
  278. throw new TypeError("You must supply a name when creating a logger.");
  279. }
  280. var logger = _loggersByName[name];
  281. if (!logger) {
  282. logger = _loggersByName[name] = new Logger(
  283. name,
  284. defaultLogger.methodFactory
  285. );
  286. }
  287. return logger;
  288. };
  289. // Grab the current global log variable in case of overwrite
  290. var _log = (typeof window !== undefinedType) ? window.log : undefined;
  291. defaultLogger.noConflict = function() {
  292. if (typeof window !== undefinedType &&
  293. window.log === defaultLogger) {
  294. window.log = _log;
  295. }
  296. return defaultLogger;
  297. };
  298. defaultLogger.getLoggers = function getLoggers() {
  299. return _loggersByName;
  300. };
  301. // ES6 default export, for compatibility
  302. defaultLogger['default'] = defaultLogger;
  303. return defaultLogger;
  304. }));