Logger
Logger that can handle async tasks.
-
Provides normal logging API: log level, tag logger instance, custom log data
-
Provides API for reporting async events that can be later handled by custom appender.
-
Provides a default appender that uses console for output.
-
Modular and configurable
-
Small size
Installation
npm install --save @niceties/logger
Example
import { createLogger } from '@niceties/logger';
const logger = createLogger();
try {
logger.start('starting something');
...
...
logger.finish('finished something');
} catch(e) {
logger.finish('finished something', 3);
}
API
Logger factory:
function createLogger<ErrorContext = Error>(...args: [] | [string | Identity | undefined] | [string, Identity]): ((message: string, loglevel?: LogLevel, context?: ErrorContext | undefined) => void) & {
start(message: string, loglevel?: LogLevel | undefined, context?: ErrorContext | undefined): void;
update(message: string, loglevel?: LogLevel | undefined, context?: ErrorContext | undefined): void;
finish(message: string, loglevel?: LogLevel | undefined, context?: ErrorContext | undefined): void;
appender(appender?: Appender<ErrorContext> | undefined): (message: LogMessage<ErrorContext>) => void;
};
Will return a logger instance that can be viewed as an entry for a single async task.
const logger = createLogger('tag');
const logger2 = createLogger(logger);
const logger3 = createLogger('tag2', logger);
tag
can be used to distinguish between async tasks (will be provided to appender).
logger can be used as parent of another logger (will be provided as parentId to appender).
const log = createLogger();
try {
log('some message');
} catch (e) {
log('some message', 1 , e);
}
Logger can be used as a function that logs message or error with context. Context type can be defined during creation of the logger (only in typescript).
const log = createLogger<Context>();
try {
log('some message');
} catch (e: Context) {
log('some message', LogLevel.info, e);
}
start(message: string, loglevel?: LogLevel | undefined, context?: ErrorContext | undefined): void;
Emits a start event inside a logger. If loglevel provided it will be remembered and used as default loglevel in subsequent events in the same logger instance. Default loglevel (if argument is not provided) is info
.
update(message: string, loglevel?: LogLevel | undefined, context?: ErrorContext | undefined): void;
Emits update event. Can be used to inform an user that we are doing something else in the same async task. loglevel used to redefine default loglevel.
finish(message: string, loglevel?: LogLevel | undefined, context?: ErrorContext | undefined): void;
Emits finish event. Can be used to inform an user that the task finished. loglevel is optional and equals initial loglevel if omitted.
const logger = createLogger();
logger.appender(someFancyAppender);
Sets a different appender for the specific instance of the logger.
const logger = createLogger();
const appender = logger.appender();
Returns current appender for the specific instance of the logger.
Log levels
const enum LogLevel {
verbose,
info,
warn,
error
}
Setting another appender
User or another library can set another appender by calling:
function appender<ErrorContext = Error>(appender?: Appender<ErrorContext>): Appender<any>;
where appender is a function with following type
(message: LogMessage<ErrorContext>) => void;
const enum Action {
start,
update,
success,
fail
}
type LogMessage<ErrorContext = Error> = {
inputId: number;
loglevel: LogLevel;
message: string;
action: Action.start | Action.update | Action.finish;
tag?: string;
parentId?: string;
ref: WeakRef<never>;
} | {
inputId?: number;
loglevel: LogLevel;
message: string;
action: Action.log;
tag?: string;
parentId?: string;
ref?: WeakRef<never>;
context?: ErrorContext;
};
Same appender function without arguments can be used to get the current appender.
FAQ
Can I use more than 4 log levels
Despite the fact loglevel defined as an enum it is just a number. Logger does not make assumptions about loglevels besides defining default loglevel as 1 (LogLevel.info).
It is generally safe to expand loglevels into both positive and negative range (finer debug messages) as far as appender takes them into account.
As an example:
const log = createLogger();
log('some message', -1);
will send a log message with finer loglevel than verbose through appender but default appender will ignore it.
Can I use multiple appenders?
It is possible using combineAppenders and appender functions:
import { createLogger, appender } from "@niceties/logger";
import { combineAppenders } from "@niceties/logger/appender-utils";
appender(combineAppenders(appender(), appender2));
Can I set filter for certain loglevel
It is possible using filterMessages and appender functions:
import { filterMessages } from "@niceties/logger/appender-utils";
let desiredLoglevel = 0;
appender(filterMessages((msg) => msg.loglevel >= desiredLoglevel, appender()));
function setLoglevel(loglevel) {
desiredLoglevel = loglevel;
}
Sub-packages
Default sub-package '@niceties/logger'
exports types, createLogger()
factory and appender()
function.
Sub-package '@niceties/logger/default-formatting'
exports formatting constants that are part of default configuration of the console appender.
Sub-package '@niceties/logger/core'
exports createLogger()
factory.
Sub-package '@niceties/logger/simple'
exports createLogger()
factory.
Sub-package '@niceties/logger/console-appender'
exports createConsoleAppender()
factory.
Sub-package '@niceties/logger/format-utils'
exports createFormatter()
and terminalSupportsUnicode()
functions.
Sub-package '@niceties/logger/global-appender'
exports appender()
and globalAppender
.
Sub-package '@niceties/logger/appender-utils'
exports combineAppenders()
and filterMessages()
.
simple
(default), core
and console-appender
exist as umd packages as well but probably require some effort to consume them.
Prior art
License
MIT