loglevelnext
Version:
A modern logging library for Node.js and modern browsers that provides log level mapping to the console
285 lines (279 loc) • 8.25 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
LogLevel: () => LogLevel,
MethodFactory: () => MethodFactory,
PrefixFactory: () => PrefixFactory,
default: () => src_default,
defaultLevels: () => defaultLevels
});
module.exports = __toCommonJS(src_exports);
// src/MethodFactory.ts
var defaultLevels = {
TRACE: 0,
DEBUG: 1,
INFO: 2,
WARN: 3,
ERROR: 4,
SILENT: 5
};
var levels = Symbol("log-levels");
var instance = Symbol("log-instance");
var noop = () => {
};
var MethodFactory = class {
constructor(logger) {
this[instance] = logger;
this[levels] = defaultLevels;
}
// @ts-ignore
get levels() {
return this[levels];
}
get logger() {
return this[instance];
}
get methods() {
return Object.keys(this.levels).map((key) => key.toLowerCase()).filter((key) => key !== "silent");
}
set logger(logger) {
this[instance] = logger;
}
// eslint-disable-next-line class-methods-use-this
bindMethod(obj, methodName) {
const method = obj[methodName];
if (typeof method.bind === "function") {
return method.bind(obj);
}
try {
return Function.prototype.bind.call(method, obj);
} catch (e) {
return function result() {
return Function.prototype.apply.apply(method, [obj, arguments]);
};
}
}
distillLevel(level) {
let value;
if (typeof level === "string") {
const levels2 = this.levels;
value = levels2[level.toUpperCase()];
} else {
value = level;
}
if (this.levelValid(value))
return value;
return null;
}
levelValid(level) {
const max = Math.max(...Object.values(this.levels));
if (typeof level === "number" && level >= 0 && level <= max) {
return true;
}
return false;
}
/**
* Build the best logging method possible for this env
* Wherever possible we want to bind, not wrap, to preserve stack traces.
* Since we're targeting modern browsers, there's no need to wait for the
* console to become available.
*/
// eslint-disable-next-line class-methods-use-this
make(methodName) {
const target = console;
if (typeof target[methodName] !== "undefined") {
return this.bindMethod(target, methodName);
} else if (typeof console.log !== "undefined") {
return this.bindMethod(target, "log");
}
return noop;
}
replaceMethods(logLevel) {
const level = this.distillLevel(logLevel);
if (level === null) {
throw new Error(`loglevelnext: replaceMethods() called with invalid level: ${logLevel}`);
}
if (!this.logger || this.logger.type !== "LogLevel") {
throw new TypeError(
"loglevelnext: Logger is undefined or invalid. Please specify a valid Logger instance."
);
}
this.methods.forEach((methodName) => {
const { [methodName.toUpperCase()]: methodLevel } = this.levels;
this.logger[methodName] = methodLevel < level ? noop : this.make(methodName);
});
this.logger.log = this.logger.debug;
}
};
instance, levels;
// src/PrefixFactory.ts
var defaults = {
level: (opts) => `[${opts.level}]`,
name: (opts) => opts.logger.name,
template: "{{time}} {{level}} ",
time: () => (/* @__PURE__ */ new Date()).toTimeString().split(" ")[0]
};
var PrefixFactory = class extends MethodFactory {
constructor(logger, options) {
super(logger);
this.options = Object.assign({}, defaults, options);
}
interpolate(level) {
return this.options.template.replace(/{{([^{}]*)}}/g, (stache, prop) => {
const fn = this.options[prop];
if (typeof fn === "function") {
return fn({ level, logger: this.logger });
}
return stache;
});
}
make(methodName) {
const og = super.make(methodName);
return (...args) => {
const output = this.interpolate(methodName);
const [first] = args;
if (typeof first === "string") {
args[0] = output + first;
} else {
args.unshift(output);
}
og(...args);
};
}
};
// src/LogLevel.ts
var defaults2 = {
factory: void 0,
level: "warn",
name: (+/* @__PURE__ */ new Date()).toString(),
prefix: void 0
};
var LogLevel = class {
constructor(options) {
this.type = "LogLevel";
this.options = Object.assign({}, defaults2, options);
this.methodFactory = options.factory;
if (!this.methodFactory) {
const factory = options.prefix ? new PrefixFactory(this, options.prefix) : new MethodFactory(this);
this.methodFactory = factory;
}
if (!this.methodFactory.logger) {
this.methodFactory.logger = this;
}
this.name = options.name || "<unknown>";
this.level = this.options.level ?? "trace";
}
get level() {
return this.currentLevel;
}
get levels() {
return this.methodFactory.levels;
}
get factory() {
return this.methodFactory;
}
set factory(factory) {
factory.logger = this;
this.methodFactory = factory;
this.methodFactory.replaceMethods(this.level);
}
set level(logLevel) {
const level = this.methodFactory.distillLevel(logLevel);
if (level === false || level == null) {
throw new RangeError(`loglevelnext: setLevel() called with invalid level: ${logLevel}`);
}
this.currentLevel = level;
this.methodFactory.replaceMethods(level);
const max = Math.max(...Object.values(this.levels));
if (typeof console === "undefined") {
process.stdout.write("loglevelnext: console is undefined. The log will produce no output.\n");
} else if (level > max) {
console.warn(
`The log level has been set to a value greater than 'silent'. The log will produce no output.`
);
}
}
disable() {
const levels2 = this.levels;
if (levels2.SILENT) {
this.level = levels2.SILENT;
} else {
console.warn(
`loglevelnext: no 'silent' level defined. The log cannot be disabled. You may want to override the 'disable' method.`
);
}
}
enable() {
const levels2 = this.levels;
if (typeof levels2.TRACE !== "undefined") {
this.level = levels2.TRACE;
} else {
console.warn(
`loglevelnext: no 'trace' level defined. The log cannot be enabled. You may want to override the 'trace' method.`
);
}
}
};
// src/index.ts
var factories = Symbol("log-factories");
var DefaultLogger = class extends LogLevel {
constructor() {
super({ name: "default" });
this.cache = { default: this };
this[factories] = { MethodFactory, PrefixFactory };
}
get factories() {
return this[factories];
}
get loggers() {
return this.cache;
}
create(opts) {
let options;
if (typeof opts === "string") {
options = { name: opts };
} else {
options = Object.assign({}, opts);
}
if (!options.id) {
options.id = options.name.toString();
}
const { name, id } = options;
const defaults3 = { level: this.level };
if (typeof name !== "string" || !name || !name.length) {
throw new TypeError("You must supply a name when creating a logger.");
}
let logger = this.cache[id];
if (!logger) {
logger = new LogLevel(Object.assign({}, defaults3, options));
this.cache[id] = logger;
}
return logger;
}
};
var src_default = new DefaultLogger();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
LogLevel,
MethodFactory,
PrefixFactory,
defaultLevels
});