Extensible logging module for Node, Deno and browsers. Inspired by Deno std/log and .Net Core
ILogger
.
- Provide a simpel, clean, extensible logging api.
- Class based, fluent interface api.
- Zero dependencies.
- Support for log scoping via contexts.
- Support for different log levels.
- Support for custom handlers so the api can be used in a wide range of szenarios.
- Written in TypeScript with a fully typed, class based api.
- Create custom log handlers. Included are a
ConsoleHandler
andTestHandler
. - Six different log levels.
TRACE
,DEBUG
,INFO
,WARNING
,ERROR
andCRITICAL
. - Fluent interface for easy configuration.
- Manage multiple logger instances globally within the
LoggerStore
.
- Format function for handlers to custom format the logs.
- Exported default logger which can be overwritten.
- More handlers directly provided.
- Your idea? Let me know!
More examples can be found in the
examples
folder.
Basic usage:
import { Logger, ConsoleHandler, LogLevel } from "logging-library";
// Create a logger instance and attach a console handler with level INFO as well
// as a custom handler with level WARN.
const logger = new Logger()
.addHandler(new ConsoleHandler(LogLevel.INFO))
.addHandler(new CustomHandler(LogLevel.WARN));
// addHandler takes an optional second argument which determines where the handler is
// actually added. Might be useful to only add a logger in Development. Example:
const logger = new Logger().addHandler(
new ConsoleHandler(LogLevel.INFO),
process.env.NODE_ENV === "development"
);
// Creates a LogRecord with level INFO that is passed to all handlers.
logger.info("Some log using the default context.");
// [Default] Some log using the default context.
// Create a scoped logger that inherits its config from `logger`. Sets the context
// to 'Authentication' in all LogRecords created by this logger.
const scoped = logger.withContext("Authentication");
scoped.warning("This log is using the 'Authentication' context");
// [Authentication] This log is using the 'Authentication' context
scoped.debug("Some debug information");
// No log output, since the ConsoleHandler requires at log level INFO
Usage with the LoggerStore
:
import { Logger, ConsoleHandler, LogLevel, LoggerStore } from "logging-library";
// Create a logger instance and attach a console handler with level INFO.
const logger = new Logger().addHandler(new ConsoleHandler(LogLevel.INFO));
LoggerStore.add("console", logger);
// Somewhere else in a file far far away...
const logger = LoggerStore.get("console");
// Start logging
logger.info("Some log using the default context.");
// [Default] Some log using the default context.
// And/Or create scopes
const scoped = logger.withContext("Authentication");
// Nobody stops you from storing the scoped logger if needed...
LoggerStore.add("auth", scoped);
Currently this library supports a ConsoleHandler
and TestHandler
with more
to come.
This library is build for extensible so you are welcome to create you custom handlers to meet you needs. More exotic handlers might send the logs to analytics or an error reporting tool.
Every handler must implement the ILogHandler
interface to be usable.
interface ILogHandler {
readonly level: LogLevel;
handle(record: ILogRecord): void;
}
This library provides an abstract BaseHandler
class. It takes care of
implementing the interface and filters all LogRecords
that does not meet the
required level. If you extend this class you only have to implement a log
method which is called for all passed LogRecords
.
import { BaseHandler } from "logging-library";
class CustomHandler extends BaseHandler {
constructor(level: LogLevel) {
super(level);
}
// Method can also be public. I recommend protected as it does not need to be
// visible when instantiated.
protected log(record: ILogRecord) {
// ... Do whatever you like with the record. See src/handlers for inspiration.
}
}
Api (Also see reference)
Enum containing all valid log levels. Handlers that require a higher level than provided in a log record will not handle the record. All enum values from low to high:
TRACE = 10
DEBUG = 20
INFO = 30
WARNING = 40
ERROR = 50
CRITICAL = 60
Interface that every logger implements.
interface ILogger {
trace(msg: string): void;
debug(msg: string): void;
info(msg: string): void;
warning(msg: string): void;
error(err: string | Error): void;
critical(err: string | Error): void;
withContext(context: string): ILogger;
addHandler(
handler: ILogHandler,
condition?: boolean | (() => boolean)
): ILogger;
readonly context: string;
}
Little helper class that uses a static map to store loggers globally. Using the same key twice, overrides a logger.
class LoggerStore {
static add(key: string, logger: ILogger): void;
static get(key: string): ILogger | undefined;
static remove(key: string): boolean;
}
Logs records the the corresponding method on the console
object.
Output format: ISO-time-string [context] message
The ConsoleHandler
classed has a static toggle
method with might be used in
a browser to toggle of all logs in production but toggle them back on with a
global function if needed. Example for a global function in Typescript:
declare global {
function toggleConsoleLogging(active?: boolean): void;
}
globalThis.toggleConsoleLogging = ConsoleHandler.toggle;
Handler that writes all logs into a message queue which. Allows for assertions about logs during testing.
class TestHandler extends BaseHandler {
public readonly records: ILogRecord[] = [];
constructor(level: LogLevel = LogLevel.TRACE) {
super(level);
}
protected log(record: ILogRecord) {
this.records.push(record);
}
}