createConsoleLogger.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { LogType } = require("./Logger");
  7. /**
  8. * @import {
  9. * FilterItemTypes,
  10. * FilterTypes
  11. * } from "../../declarations/WebpackOptions"
  12. */
  13. /** @import { LogTypeEnum, Args } from "./Logger" */
  14. /** @typedef {(item: string) => boolean} FilterFunction */
  15. /** @typedef {(value: string, type: LogTypeEnum, args?: Args) => void} LoggingFunction */
  16. /**
  17. * Defines the logger console type used by this module.
  18. * @typedef {object} LoggerConsole
  19. * @property {() => void} clear
  20. * @property {() => void} trace
  21. * @property {(...args: Args) => void} info
  22. * @property {(...args: Args) => void} log
  23. * @property {(...args: Args) => void} warn
  24. * @property {(...args: Args) => void} error
  25. * @property {(...args: Args) => void=} debug
  26. * @property {(...args: Args) => void=} group
  27. * @property {(...args: Args) => void=} groupCollapsed
  28. * @property {(...args: Args) => void=} groupEnd
  29. * @property {(...args: Args) => void=} status
  30. * @property {(...args: Args) => void=} profile
  31. * @property {(...args: Args) => void=} profileEnd
  32. * @property {(...args: Args) => void=} logTime
  33. */
  34. /**
  35. * Defines the logger options type used by this module.
  36. * @typedef {object} LoggerOptions
  37. * @property {false | true | "none" | "error" | "warn" | "info" | "log" | "verbose"} level loglevel
  38. * @property {FilterTypes | boolean} debug filter for debug logging
  39. * @property {LoggerConsole} console the console to log to
  40. */
  41. /**
  42. * Filter to function.
  43. * @param {FilterItemTypes} item an input item
  44. * @returns {FilterFunction | undefined} filter function
  45. */
  46. const filterToFunction = (item) => {
  47. if (typeof item === "string") {
  48. const regExp = new RegExp(
  49. `[\\\\/]${item.replace(/[-[\]{}()*+?.\\^$|]/g, "\\$&")}([\\\\/]|$|!|\\?)`
  50. );
  51. return (ident) => regExp.test(ident);
  52. }
  53. if (item && typeof item === "object" && typeof item.test === "function") {
  54. return (ident) => item.test(ident);
  55. }
  56. if (typeof item === "function") {
  57. return item;
  58. }
  59. if (typeof item === "boolean") {
  60. return () => item;
  61. }
  62. };
  63. /**
  64. * Enumerates the available values.
  65. * @enum {number}
  66. */
  67. const LogLevel = {
  68. none: 6,
  69. false: 6,
  70. error: 5,
  71. warn: 4,
  72. info: 3,
  73. log: 2,
  74. true: 2,
  75. verbose: 1
  76. };
  77. /**
  78. * Returns logging function.
  79. * @param {LoggerOptions} options options object
  80. * @returns {LoggingFunction} logging function
  81. */
  82. module.exports = ({ level = "info", debug = false, console }) => {
  83. const debugFilters =
  84. /** @type {FilterFunction[]} */
  85. (
  86. typeof debug === "boolean"
  87. ? [() => debug]
  88. : /** @type {FilterItemTypes[]} */ ([
  89. ...(Array.isArray(debug) ? debug : [debug])
  90. ]).map(filterToFunction)
  91. );
  92. const loglevel = LogLevel[`${level}`] || 0;
  93. /**
  94. * Processes the provided name.
  95. * @param {string} name name of the logger
  96. * @param {LogTypeEnum} type type of the log entry
  97. * @param {Args=} args arguments of the log entry
  98. * @returns {void}
  99. */
  100. const logger = (name, type, args) => {
  101. /**
  102. * Returns labeled args.
  103. * @template T
  104. * @returns {[string?, ...T[]]} labeled args
  105. */
  106. const labeledArgs = () => {
  107. if (Array.isArray(args)) {
  108. if (args.length > 0 && typeof args[0] === "string") {
  109. return [`[${name}] ${args[0]}`, ...args.slice(1)];
  110. }
  111. return [`[${name}]`, ...args];
  112. }
  113. return [];
  114. };
  115. const debug = debugFilters.some((f) => f(name));
  116. switch (type) {
  117. case LogType.debug:
  118. if (!debug) return;
  119. if (typeof console.debug === "function") {
  120. console.debug(...labeledArgs());
  121. } else {
  122. console.log(...labeledArgs());
  123. }
  124. break;
  125. case LogType.log:
  126. if (!debug && loglevel > LogLevel.log) return;
  127. console.log(...labeledArgs());
  128. break;
  129. case LogType.info:
  130. if (!debug && loglevel > LogLevel.info) return;
  131. console.info(...labeledArgs());
  132. break;
  133. case LogType.warn:
  134. if (!debug && loglevel > LogLevel.warn) return;
  135. console.warn(...labeledArgs());
  136. break;
  137. case LogType.error:
  138. if (!debug && loglevel > LogLevel.error) return;
  139. console.error(...labeledArgs());
  140. break;
  141. case LogType.trace:
  142. if (!debug) return;
  143. console.trace();
  144. break;
  145. case LogType.groupCollapsed:
  146. if (!debug && loglevel > LogLevel.log) return;
  147. if (!debug && loglevel > LogLevel.verbose) {
  148. if (typeof console.groupCollapsed === "function") {
  149. console.groupCollapsed(...labeledArgs());
  150. } else {
  151. console.log(...labeledArgs());
  152. }
  153. break;
  154. }
  155. // falls through
  156. case LogType.group:
  157. if (!debug && loglevel > LogLevel.log) return;
  158. if (typeof console.group === "function") {
  159. console.group(...labeledArgs());
  160. } else {
  161. console.log(...labeledArgs());
  162. }
  163. break;
  164. case LogType.groupEnd:
  165. if (!debug && loglevel > LogLevel.log) return;
  166. if (typeof console.groupEnd === "function") {
  167. console.groupEnd();
  168. }
  169. break;
  170. case LogType.time: {
  171. if (!debug && loglevel > LogLevel.log) return;
  172. const [label, start, end] =
  173. /** @type {[string, number, number]} */
  174. (args);
  175. const ms = start * 1000 + end / 1000000;
  176. const msg = `[${name}] ${label}: ${ms} ms`;
  177. if (typeof console.logTime === "function") {
  178. console.logTime(msg);
  179. } else {
  180. console.log(msg);
  181. }
  182. break;
  183. }
  184. case LogType.profile:
  185. if (typeof console.profile === "function") {
  186. console.profile(...labeledArgs());
  187. }
  188. break;
  189. case LogType.profileEnd:
  190. if (typeof console.profileEnd === "function") {
  191. console.profileEnd(...labeledArgs());
  192. }
  193. break;
  194. case LogType.clear:
  195. if (!debug && loglevel > LogLevel.log) return;
  196. if (typeof console.clear === "function") {
  197. console.clear();
  198. }
  199. break;
  200. case LogType.status:
  201. if (!debug && loglevel > LogLevel.info) return;
  202. if (typeof console.status === "function") {
  203. if (!args || args.length === 0) {
  204. console.status();
  205. } else {
  206. console.status(...labeledArgs());
  207. }
  208. } else if (args && args.length !== 0) {
  209. console.info(...labeledArgs());
  210. }
  211. break;
  212. default:
  213. throw new Error(`Unexpected LogType ${type}`);
  214. }
  215. };
  216. return logger;
  217. };