A logging library for libraries.
âś” Multiple levels based on audience. Public, Developers, Internal
âś” Colorful browser logs
âś” CommonJS
âś” ES Modules
âś” TypeScript
We can create loggers which are essentially nested from their parent.
import { createLibraryLoggerProvider } from "librarylog";
// create logger provider
const provider = createLibraryLoggerProvider();
// root logger
const logger = provider.getLogger()
// create nested loggers
const appLogger = logger.named("App")
// create nested loggers with a name and a key
const pageLogger = appLogger.named("Page", page.id)
We can log for a variety of audiences.
import { createLibraryLoggerProvider } from "librarylog";
// create logger provider
const provider = createLibraryLoggerProvider();
// root logger
const logger = provider.getLogger()
// create nested loggers
const appLogger = logger.named("App")
// create nested loggers with a name and a key
const pageLogger = appLogger.named("Page", page.id)
See the breadth of logger functions with example messages. All log functions accept one message and one optional arguments object.
// internal log functions
appLogger._error("We've experienced a general problem")
appLogger._hmm("This doesn't look right")
appLogger._todo("I'm not finished")
appLogger._kapow("Lookie here! I guess we do execute this code")
appLogger._warn("I'm not finished")
appLogger._debug("Opening page", { pageId: "..." })
appLogger._trace("User mouse clicked", { mouseEvent: "..." })
// developer log functions
appLogger.errorDev("Data passed in was malformed")
appLogger.warnDev("Challenge with loading exports sourcemaps. Ensure you're compiling with esm: true")
appLogger.debugDev("Page loaded", { pageId: "...", duration: "...", objects: 120 })
appLogger.traceDev("Loading page's external object", { pageId: "...", objectId: "..." })
// public log functions
appLogger.errorPublic("Something that just cannot go unnoticed!")
appLogger.warnPublic("Something so so important!")
All log functions also come with a .lazy.<logfn>(message, () => args)
version.
// execute the inner function only if the log is included in logging
appLogger.lazy.debugDev("Page loaded", () => ({ pageId: "...", pageMeta: calculatePageMeta() }))
In many places in your code such as in utility functions, you don't know which
audience the logs are for. So, in this case, you can use .downgrade.<audience>()
to produce a IUtilLogger
(which is also nameable).
It looks like the following:
export interface IUtilLogger {
/** Usually equivalent to `console.error`. */
error(message: string, args?: LibraryLoggable): void;
/** Usually equivalent to `console.warn`. */
warn(message: string, args?: LibraryLoggable): void;
/** Usually equivalent to `console.info`. */
debug(message: string, args?: LibraryLoggable): void;
/** Usually equivalent to `console.debug`. */
trace(message: string, args?: LibraryLoggable): void;
named(name: string, key?: string): IUtilLogger;
}
For example,
// now, any logs that `cssRenderHelpers` wants to surface, will go through
// the `internal` audience.
const cssLogger = appLogger.downgrade.internal()
cssRenderHelpers.convertBezier(cssLogger, "...")
import { createLibraryLoggerProvider } from "librarylog";
// create logger provider
const provider = createLibraryLoggerProvider();
// set custom logging behaviors (filtering and such)
provider.configureFiltering({
// disable style to the console (if the logger does not support style, this won't have an effect)
consoleStyle: false,
// include logs made for the dev audience
dev: true,
// include logs made for the internal audience
internal: true,
// configure the behavior of inclusion based on source of the logs
include(source) {
if (source.names.find(n => n.name === "XYZSystem")) {
// include internal logs for XYZSystem children
return {
internal: true
}
}
if (source.names.find(n => n.name === "Rendering")) {
// suppress all logs under the "Rendering" tree
return {
internal: false,
dev: false,
min: Infinity,
}
}
if (source.names.find(n => n.name === "Page" && n.key === "page_ajkwhloieuw8990se")) {
// enable all logs for page "page_ajkwhloieuw8990se"
// this source would have been constructed via something like `parentLogger.named("Page", page.id)`
return {
internal: true,
min: 0,
}
}
},
});
import { createLibraryLoggerProvider, LibraryLoggerLevel } from "librarylog";
// create logger provider
const logger = createLibraryLoggerProvider();
// set a custom console
logger.configureConsole({
type: "console",
console: console,
// disable colorful styling
style: false,
});
// disable console styling, and set default console
logger.configureConsole({
type: "console",
style: false,
});
// set your own keyed logger
logger.configureConsole({
type: "keyed",
keyed(nameAndKeys) {
const prefix = nameAndKeys
.map((a) => (a.key ? `${a.name}#${a.key}` : a.name))
.join(" ");
return {
error(meta, message, args) {
console.error(
meta.audience,
meta.category,
LibraryLoggerLevel[meta.level],
prefix,
message,
...(args ? [args] : [])
);
},
warn(meta, message, args) {
console.warn(
meta.audience,
meta.category,
LibraryLoggerLevel[meta.level],
prefix,
message,
...(args ? [args] : [])
);
},
debug(meta, message, args) {
console.info(
meta.audience,
meta.category,
LibraryLoggerLevel[meta.level],
prefix,
message,
...(args ? [args] : [])
);
},
trace(meta, message, args) {
console.debug(
meta.audience,
meta.category,
LibraryLoggerLevel[meta.level],
prefix,
message,
...(args ? [args] : [])
);
},
};
},
});
This project is licensed under the terms of the MIT license.