unilogr
TypeScript icon, indicating that this package has built-in type declarations

0.0.27 • Public • Published

UniLogr: a Universal Logger

Version npm

UniLogr is a logger for both Node.js and Browser inspired by Winston.

Motivation

This project aims to provide a simple but powerful logger that works on both Node.js and the Browser. The logger should be flexible and convenient, allowing the user to add custom outputs and create sub-loggers.

Installation

Install with NPM:

npm install unilogr

Install with Yarn:

yarn add unilogr

Install with PNPM:

pnpm add unilogr

Usage

Creating a logger

A logger is constructed with a sequence of operations that will be executed on each log. The operation writeTo is used to write a log to an output stream.

import {
  addInterval,
  addTimestamp,
  capitalizeField,
  colorizeField,
  ConsoleOutput,
  Logger,
  markSlot,
  writeTo,
} from 'unilogr';

const logger = new Logger([
  capitalizeField('level'), // Capitalize the "level" field
  colorizeField('level'), // Colorize the "level" field according to severity
  addTimestamp(), // Add the "timestamp" field
  addInterval(), // Add the "interval" field

  markSlot(), // Mark this spot for extensions

  ({ timestamp, level, message, interval }) =>
    `${timestamp} [${level}]: ${message} (${interval})`, // Format message

  writeTo(new ConsoleOutput()), // Write formatted message to console
]);

logger.info('Test'); // 2022-11-01 19:11:47 [INFO]: Test (+0ms)

Log levels

UniLogr uses the following log levels:

logger.error('Error');
logger.warn('Warn');
logger.info('Info');
logger.debug('Debug');
logger.verbose('Verbose');

The Info object

The info object is a plain JavaScript object that contains at least the following fields:

const info = {
  level: 'info', // Customizable field (colorize it, capitalize it, etc.)
  message: 'Example', // Customizable field

  [LEVEL_SYMBOL]: 'info', // Read-only field used to determine the log level
  [OUTPUT_SYMBOL]: '[INFO]: Example', // String that will be written to the output
  [ARGS_SYMBOL]: [], // Arguments for messages containing %s, %d, %o, etc.
};

String interpolation

UniLogr supports string interpolation using %s, %d, %o, etc. as placeholders. Arguments without corresponding placeholders are merged into the info object.

const logger = new Logger([
  (info) => {
    info[OUTPUT_SYMBOL] = `[${info.level}] (${info.ctx}): ${info.message}`;
  },

  writeTo(new ConsoleOutput()),
]);

logger.info('Origin is %s', 'http://localhost', { ctx: 'CORS' });
// [info] (CORS): Origin is http://localhost

Filtering logs

You can discard logs by returning false in an operation:

const logger = new Logger([
  (info) => info.ctx === 'Auth', // Accept only logs with 'Auth' context

  // ...
]);

logger.info('Server started.', { ctx: 'Startup' }); // Discarded

logger.info('Refreshing tokens...', { ctx: 'Auth' }); // Accepted

UniLogr provides the utility function discardLessSevereThan() to easily filter logs by level of severity:

const logger = new Logger([
  discardLessSevereThan('warn'), // Discard logs with level less severe than 'warn'
  // Same as: (info) => levels[info[LEVEL_SYMBOL]] <= levels['warn']

  // ...
]);

Extending a logger

A logger can be extended by inserting more operations. Operations are inserted in slots marked by markSlot().

const mainLogger = new Logger([
  addContext('Main context'),

  markSlot(), // <<< Extension operations are inserted here

  ({ timestamp, message, ctx }) =>
    `${timestamp}${ctx ? ` (${ctx})` : ''}: ${message}`,
]);

mainLogger.info('Main logger test');
// 2029-05-02 11:18:41 (Main context): Main logger test

const subLogger = mainLogger.extend([addContext('Sub context')]);

subLogger.info('Sub logger test');
// 2029-05-02 11:18:41 (Main context > Sub context): Sub logger test

The utility function logger.sub(context) helps to easily extend loggers by adding a subcontext:

const authLogger = mainLogger.sub('Auth');
// Appends the context to the field "ctx" in the info object.
// Same as mainLogger.extend([addContext('Auth')])

You can create and extend multiple slots by giving them different names.

const mainLogger = new Logger([
  // ...
  markSlot(), // name: 'defaultSlot'
  // ...
  markSlot('slot1'),

  ({ timestamp, level, message, ctx }) =>
    `${timestamp} [${level}]${ctx ? ` (${ctx})` : ''}: ${message}`,

  markSlot('slot2'),
]);

const subLogger = mainLogger.extend({
  defaultSlot: [addContext('server.ts')],

  slot1: [addTimestamp()],

  slot2: [writeTo(new FileOutput('logs.txt'))],
});

License

MIT License © 2022 Gustavo Toyota

Package Sidebar

Install

npm i unilogr

Weekly Downloads

5

Version

0.0.27

License

ISC

Unpacked Size

63.5 kB

Total Files

39

Last publish

Collaborators

  • gustavotoyota