From d80989bfbfd2a28f2b06b2e2d28b52326bf9579c Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Sun, 21 Jul 2019 22:28:46 +0200 Subject: [PATCH] allow to configure runtime logging --- lib/Compiler.js | 11 +- lib/logging/logToConsole.js | 263 +++++++++++++++++++++++------------- lib/logging/runtime.js | 22 ++- 3 files changed, 196 insertions(+), 100 deletions(-) diff --git a/lib/Compiler.js b/lib/Compiler.js index a2b4c7fb1..8d5caf6e9 100644 --- a/lib/Compiler.js +++ b/lib/Compiler.js @@ -27,12 +27,17 @@ const ResolverFactory = require("./ResolverFactory"); const RequestShortener = require("./RequestShortener"); const { makePathsRelative } = require("./util/identifier"); const ConcurrentCompilationError = require("./ConcurrentCompilationError"); -const { Logger, LogType } = require("./logging/Logger"); +const { Logger } = require("./logging/Logger"); const logToConsole = require("./logging/logToConsole"); /** @typedef {import("../declarations/WebpackOptions").Entry} Entry */ /** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */ +const infrastructureLogger = logToConsole({ + level: "log", + debug: false +}); + /** * @typedef {Object} CompilationParams * @property {NormalModuleFactory} normalModuleFactory @@ -221,9 +226,7 @@ class Compiler extends Tapable { } } if (this.hooks.log.call(name, type, args) === undefined) { - // ignore debug logging by default - if (type === LogType.debug) return; - logToConsole(name, type, args); + infrastructureLogger(name, type, args); } }); } diff --git a/lib/logging/logToConsole.js b/lib/logging/logToConsole.js index 550ac3e65..439326ee7 100644 --- a/lib/logging/logToConsole.js +++ b/lib/logging/logToConsole.js @@ -8,103 +8,176 @@ const { LogType } = require("./Logger"); /** @typedef {import("./Logger").LogTypeEnum} LogTypeEnum */ +/** @typedef {function(string): boolean} FilterFunction */ +/** @typedef {RegExp|FilterFunction|string|boolean} FilterInputItem */ +/** @typedef {FilterInputItem[]|FilterInputItem} FilterInput */ /** - * @param {string} name name of the logger - * @param {LogTypeEnum} type type of the log entry - * @param {any[]} args arguments of the log entry + * @typedef {Object} LoggerOptions + * @property {false|true|"error"|"warn"|"info"|"log"|"verbose"} options.level loglevel + * @property {FilterInput} options.debug filter for debug logging */ -module.exports = (name, type, args) => { - const labeledArgs = (prefix = "") => { - if (Array.isArray(args)) { - if (args.length > 0 && typeof args[0] === "string") { - return [`${prefix}[${name}] ${args[0]}`, ...args.slice(1)]; - } else { - return [`${prefix}[${name}]`, ...args]; - } - } else { - return []; - } - }; - switch (type) { - case LogType.debug: - // eslint-disable-next-line node/no-unsupported-features/node-builtins - if (typeof console.debug === "function") { - // eslint-disable-next-line node/no-unsupported-features/node-builtins - console.debug(...labeledArgs()); - } else { - console.log(...labeledArgs()); - } - break; - case LogType.log: - console.log(...labeledArgs()); - break; - case LogType.info: - console.info(...labeledArgs(" ")); - break; - case LogType.warn: - console.warn(...labeledArgs(" ")); - break; - case LogType.error: - console.error(...labeledArgs(" ")); - break; - case LogType.trace: - console.trace(); - break; - case LogType.group: - // eslint-disable-next-line node/no-unsupported-features/node-builtins - if (typeof console.group === "function") { - // eslint-disable-next-line node/no-unsupported-features/node-builtins - console.group(...labeledArgs()); - } else { - console.log(...labeledArgs()); - } - break; - case LogType.groupCollapsed: - // eslint-disable-next-line node/no-unsupported-features/node-builtins - if (typeof console.groupCollapsed === "function") { - // eslint-disable-next-line node/no-unsupported-features/node-builtins - console.groupCollapsed(...labeledArgs()); - } else { - console.log(...labeledArgs(" ")); - } - break; - case LogType.groupEnd: - // eslint-disable-next-line node/no-unsupported-features/node-builtins - if (typeof console.groupEnd === "function") { - // eslint-disable-next-line node/no-unsupported-features/node-builtins - console.groupEnd(); - } else { - console.log(...labeledArgs(" ")); - } - break; - case LogType.time: - console.log( - `[${name}] ${args[0]}: ${args[1] * 1000 + args[2] / 1000000}ms` - ); - break; - case LogType.profile: - // eslint-disable-next-line node/no-unsupported-features/node-builtins - if (typeof console.profile === "function") { - // eslint-disable-next-line node/no-unsupported-features/node-builtins - console.profile(...labeledArgs()); - } - break; - case LogType.profileEnd: - // eslint-disable-next-line node/no-unsupported-features/node-builtins - if (typeof console.profileEnd === "function") { - // eslint-disable-next-line node/no-unsupported-features/node-builtins - console.profileEnd(...labeledArgs()); - } - break; - case LogType.clear: - // eslint-disable-next-line node/no-unsupported-features/node-builtins - if (typeof console.clear === "function") { - // eslint-disable-next-line node/no-unsupported-features/node-builtins - console.clear(); - } - break; - default: - throw new Error(`Unexpected LogType ${type}`); + +/** + * @param {FilterInputItem} item an input item + * @returns {function(string): boolean} filter funtion + */ +const filterToFunction = item => { + if (typeof item === "string") { + const regExp = new RegExp( + `[\\\\/]${item.replace( + // eslint-disable-next-line no-useless-escape + /[-[\]{}()*+?.\\^$|]/g, + "\\$&" + )}([\\\\/]|$|!|\\?)` + ); + return ident => regExp.test(ident); + } + if (item && typeof item === "object" && typeof item.test === "function") { + return ident => item.test(ident); + } + if (typeof item === "function") { + return item; + } + if (typeof item === "boolean") { + return () => item; } }; + +/** + * @enum {number} */ +const LogLevel = { + false: 6, + error: 5, + warn: 4, + info: 3, + log: 2, + true: 2, + verbose: 1 +}; + +/** + * @param {LoggerOptions} options options object + * @returns {function(string, LogTypeEnum, any[]): void} logging function + */ +module.exports = ({ level = "info", debug = false }) => { + const debugFilters = /** @type {FilterInputItem[]} */ ([]) + .concat(debug) + .map(filterToFunction); + /** @type {number} */ + const loglevel = LogLevel[`${level}`] || 0; + + /** + * @param {string} name name of the logger + * @param {LogTypeEnum} type type of the log entry + * @param {any[]} args arguments of the log entry + * @returns {void} + */ + const logger = (name, type, args) => { + const labeledArgs = (prefix = "") => { + if (Array.isArray(args)) { + if (args.length > 0 && typeof args[0] === "string") { + return [`${prefix}[${name}] ${args[0]}`, ...args.slice(1)]; + } else { + return [`${prefix}[${name}]`, ...args]; + } + } else { + return []; + } + }; + const debug = debugFilters.some(f => f(name)); + switch (type) { + case LogType.debug: + if (!debug) return; + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.debug === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.debug(...labeledArgs()); + } else { + console.log(...labeledArgs()); + } + break; + case LogType.log: + if (!debug && loglevel > LogLevel.log) return; + console.log(...labeledArgs()); + break; + case LogType.info: + if (!debug && loglevel > LogLevel.info) return; + console.info(...labeledArgs(" ")); + break; + case LogType.warn: + if (!debug && loglevel > LogLevel.warn) return; + console.warn(...labeledArgs(" ")); + break; + case LogType.error: + if (!debug && loglevel > LogLevel.error) return; + console.error(...labeledArgs(" ")); + break; + case LogType.trace: + if (!debug) return; + console.trace(); + break; + case LogType.group: + if (!debug && loglevel > LogLevel.log) return; + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.group === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.group(...labeledArgs()); + } else { + console.log(...labeledArgs()); + } + break; + case LogType.groupCollapsed: + if (!debug && loglevel > LogLevel.log) return; + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.groupCollapsed === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.groupCollapsed(...labeledArgs()); + } else { + console.log(...labeledArgs(" ")); + } + break; + case LogType.groupEnd: + if (!debug && loglevel > LogLevel.log) return; + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.groupEnd === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.groupEnd(); + } else { + console.log(...labeledArgs(" ")); + } + break; + case LogType.time: + if (!debug && loglevel > LogLevel.log) return; + console.log( + `[${name}] ${args[0]}: ${args[1] * 1000 + args[2] / 1000000}ms` + ); + break; + case LogType.profile: + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.profile === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.profile(...labeledArgs()); + } + break; + case LogType.profileEnd: + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.profileEnd === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.profileEnd(...labeledArgs()); + } + break; + case LogType.clear: + if (!debug && loglevel > LogLevel.log) return; + // eslint-disable-next-line node/no-unsupported-features/node-builtins + if (typeof console.clear === "function") { + // eslint-disable-next-line node/no-unsupported-features/node-builtins + console.clear(); + } + break; + default: + throw new Error(`Unexpected LogType ${type}`); + } + }; + return logger; +}; diff --git a/lib/logging/runtime.js b/lib/logging/runtime.js index d4a4cdafc..f4ea3119c 100644 --- a/lib/logging/runtime.js +++ b/lib/logging/runtime.js @@ -2,14 +2,34 @@ const SyncBailHook = require("tapable/lib/SyncBailHook"); const { Logger } = require("./Logger"); const logToConsole = require("./logToConsole"); +/** @type {logToConsole.LoggerOptions} */ +let currentDefaultLoggerOptions = { + level: "info", + debug: false +}; +let currentDefaultLogger = logToConsole(currentDefaultLoggerOptions); + +/** + * @param {string} name name of the logger + * @returns {Logger} a logger + */ exports.getLogger = name => { return new Logger((type, args) => { if (exports.hooks.log.call(name, type, args) === undefined) { - logToConsole(name, type, args); + currentDefaultLogger(name, type, args); } }); }; +/** + * @param {logToConsole.LoggerOptions} options new options, merge with old options + * @returns {void} + */ +exports.configureDefaultLogger = options => { + Object.assign(currentDefaultLoggerOptions, options); + currentDefaultLogger = logToConsole(currentDefaultLoggerOptions); +}; + exports.hooks = { log: new SyncBailHook(["origin", "type", "args"]) };