Skip to content

Commit

Permalink
Merge pull request #9436 from webpack/feature/logging
Browse files Browse the repository at this point in the history
add logging API
  • Loading branch information
sokra committed Jul 23, 2019
2 parents a1f0789 + 677ccd9 commit 2fb853e
Show file tree
Hide file tree
Showing 27 changed files with 1,260 additions and 35 deletions.
45 changes: 35 additions & 10 deletions declarations/WebpackOptions.d.ts
Expand Up @@ -75,6 +75,16 @@ export type ExternalItem =
* via the `definition` "ArrayOfStringValues".
*/
export type ArrayOfStringValues = string[];
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "FilterTypes".
*/
export type FilterTypes = FilterItemTypes | FilterItemTypes[];
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "FilterItemTypes".
*/
export type FilterItemTypes = RegExp | string | ((value: string) => boolean);
/**
* One or multiple rule conditions
*
Expand Down Expand Up @@ -235,16 +245,6 @@ export type WebpackPluginFunction = (
* via the `definition` "RuleSetRules".
*/
export type RuleSetRules = RuleSetRule[];
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "FilterTypes".
*/
export type FilterTypes = FilterItemTypes | FilterItemTypes[];
/**
* This interface was referenced by `WebpackOptions`'s JSON-Schema
* via the `definition` "FilterItemTypes".
*/
export type FilterItemTypes = RegExp | string | Function;

export interface WebpackOptions {
/**
Expand Down Expand Up @@ -293,6 +293,19 @@ export interface WebpackOptions {
* Specify dependencies that shouldn't be resolved by webpack, but should become dependencies of the resulting bundle. The kind of the dependency depends on `output.libraryTarget`.
*/
externals?: Externals;
/**
* Options for infrastructure level logging
*/
infrastructureLogging?: {
/**
* Enable debug logging for specific loggers
*/
debug?: FilterTypes | boolean;
/**
* Log level
*/
level?: "none" | "error" | "warn" | "info" | "log" | "verbose";
};
/**
* Custom values available in the loader context.
*/
Expand Down Expand Up @@ -1343,6 +1356,18 @@ export interface StatsOptions {
* add the hash of the compilation
*/
hash?: boolean;
/**
* add logging output
*/
logging?: boolean | ("none" | "error" | "warn" | "info" | "log" | "verbose");
/**
* Include debug logging of specified loggers (i. e. for plugins or loaders). Filters can be Strings, RegExps or Functions
*/
loggingDebug?: FilterTypes | boolean;
/**
* add stack traces to logging output
*/
loggingTrace?: boolean;
/**
* Set the maximum number of modules to be shown
*/
Expand Down
78 changes: 78 additions & 0 deletions lib/Compilation.js
Expand Up @@ -36,6 +36,8 @@ const SortableSet = require("./util/SortableSet");
const GraphHelpers = require("./GraphHelpers");
const ModuleDependency = require("./dependencies/ModuleDependency");
const compareLocations = require("./compareLocations");
const { Logger, LogType } = require("./logging/Logger");
const ErrorHelpers = require("./ErrorHelpers");

/** @typedef {import("./Module")} Module */
/** @typedef {import("./Compiler")} Compiler */
Expand Down Expand Up @@ -95,6 +97,14 @@ const compareLocations = require("./compareLocations");
* @property {DependenciesBlockVariable[]} variables
*/

/**
* @typedef {Object} LogEntry
* @property {string} type
* @property {any[]} args
* @property {number} time
* @property {string[]=} trace
*/

/**
* @param {Chunk} a first chunk to sort by id
* @param {Chunk} b second chunk to sort by id
Expand Down Expand Up @@ -384,6 +394,9 @@ class Compilation extends Tapable {
"compilerIndex"
]),

/** @type {SyncBailHook<string, LogEntry>} */
log: new SyncBailHook(["origin", "logEntry"]),

// TODO the following hooks are weirdly located here
// TODO move them for webpack 5
/** @type {SyncHook<object, Module>} */
Expand Down Expand Up @@ -469,6 +482,8 @@ class Compilation extends Tapable {
this.warnings = [];
/** @type {Compilation[]} */
this.children = [];
/** @type {Map<string, LogEntry[]>} */
this.logging = new Map();
/** @type {Map<DepConstructor, ModuleFactory>} */
this.dependencyFactories = new Map();
/** @type {Map<DepConstructor, DependencyTemplate>} */
Expand Down Expand Up @@ -499,6 +514,69 @@ class Compilation extends Tapable {
return new Stats(this);
}

/**
* @param {string | (function(): string)} name name of the logger, or function called once to get the logger name
* @returns {Logger} a logger with that name
*/
getLogger(name) {
if (!name) {
throw new TypeError("Compilation.getLogger(name) called without a name");
}
/** @type {LogEntry[] | undefined} */
let logEntries;
return new Logger((type, args) => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compilation.getLogger(name) called with a function not returning a name"
);
}
}
let trace;
switch (type) {
case LogType.warn:
case LogType.error:
case LogType.trace:
trace = ErrorHelpers.cutOffLoaderExecution(new Error("Trace").stack)
.split("\n")
.slice(3);
break;
}
/** @type {LogEntry} */
const logEntry = {
time: Date.now(),
type,
args,
trace
};
if (this.hooks.log.call(name, logEntry) === undefined) {
if (logEntry.type === 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(`[${name}] ${logEntry.args[0]}`);
}
}
if (logEntries === undefined) {
logEntries = this.logging.get(name);
if (logEntries === undefined) {
logEntries = [];
this.logging.set(name, logEntries);
}
}
logEntries.push(logEntry);
if (logEntry.type === 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(`[${name}] ${logEntry.args[0]}`);
}
}
}
});
}

/**
* @typedef {Object} AddModuleResult
* @property {Module} module the added or existing module
Expand Down
33 changes: 33 additions & 0 deletions lib/Compiler.js
Expand Up @@ -27,6 +27,7 @@ const ResolverFactory = require("./ResolverFactory");
const RequestShortener = require("./RequestShortener");
const { makePathsRelative } = require("./util/identifier");
const ConcurrentCompilationError = require("./ConcurrentCompilationError");
const { Logger } = require("./logging/Logger");

/** @typedef {import("../declarations/WebpackOptions").Entry} Entry */
/** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
Expand Down Expand Up @@ -84,6 +85,9 @@ class Compiler extends Tapable {
/** @type {SyncHook} */
watchClose: new SyncHook([]),

/** @type {SyncBailHook<string, string, any[]>} */
infrastructurelog: new SyncBailHook(["origin", "type", "args"]),

// TODO the following hooks are weirdly located here
// TODO move them for webpack 5
/** @type {SyncHook} */
Expand Down Expand Up @@ -137,6 +141,8 @@ class Compiler extends Tapable {
/** @type {ResolverFactory} */
this.resolverFactory = new ResolverFactory();

this.infrastructureLogger = undefined;

// TODO remove in webpack 5
this.resolvers = {
normal: {
Expand Down Expand Up @@ -196,6 +202,33 @@ class Compiler extends Tapable {
this._assetEmittingWrittenFiles = new Map();
}

/**
* @param {string | (function(): string)} name name of the logger, or function called once to get the logger name
* @returns {Logger} a logger with that name
*/
getInfrastructureLogger(name) {
if (!name) {
throw new TypeError(
"Compiler.getInfrastructureLogger(name) called without a name"
);
}
return new Logger((type, args) => {
if (typeof name === "function") {
name = name();
if (!name) {
throw new TypeError(
"Compiler.getInfrastructureLogger(name) called with a function not returning a name"
);
}
}
if (this.hooks.infrastructurelog.call(name, type, args) === undefined) {
if (this.infrastructureLogger !== undefined) {
this.infrastructureLogger(name, type, args);
}
}
});
}

watch(watchOptions, handler) {
if (this.running) return handler(new ConcurrentCompilationError());

Expand Down
19 changes: 15 additions & 4 deletions lib/NormalModule.js
Expand Up @@ -147,30 +147,41 @@ class NormalModule extends Module {

createLoaderContext(resolver, options, compilation, fs) {
const requestShortener = compilation.runtimeTemplate.requestShortener;
const getCurrentLoaderName = () => {
const currentLoader = this.getCurrentLoader(loaderContext);
if (!currentLoader) return "(not in loader scope)";
return requestShortener.shorten(currentLoader.loader);
};
const loaderContext = {
version: 2,
emitWarning: warning => {
if (!(warning instanceof Error)) {
warning = new NonErrorEmittedError(warning);
}
const currentLoader = this.getCurrentLoader(loaderContext);
this.warnings.push(
new ModuleWarning(this, warning, {
from: requestShortener.shorten(currentLoader.loader)
from: getCurrentLoaderName()
})
);
},
emitError: error => {
if (!(error instanceof Error)) {
error = new NonErrorEmittedError(error);
}
const currentLoader = this.getCurrentLoader(loaderContext);
this.errors.push(
new ModuleError(this, error, {
from: requestShortener.shorten(currentLoader.loader)
from: getCurrentLoaderName()
})
);
},
getLogger: name => {
const currentLoader = this.getCurrentLoader(loaderContext);
return compilation.getLogger(() =>
[currentLoader && currentLoader.loader, name, this.identifier()]
.filter(Boolean)
.join("|")
);
},
// TODO remove in webpack 5
exec: (code, filename) => {
// @ts-ignore Argument of type 'this' is not assignable to parameter of type 'Module'.
Expand Down

0 comments on commit 2fb853e

Please sign in to comment.