Another logger in JS. This one offers a console.log-like API and formatting, colored lines and timestamps (or not if desired), all that with 0 dependencies.

Overview

hellog

Your new logger !

hellog is a general-purpose logging library.

It offers a console.log-like API and formatting, extensible type-safety colored lines and timestamps (or not if desired), all that with 0 dependencies.

  1. Installation
  2. Get started
  3. Transports and options
    1. Console
    2. File
  4. Formatters
    1. Available formatters
    2. Add custom formatters
  5. Extensibility
    1. Context elaboration
      1. Presentation
      2. Message context elaboration
    2. Transport
      1. Preparation
      2. Formatting
      3. Transportation

Install

npm install --save @steffthestunt/hellog

Get started

const { Hellog } = require('hellog');

const logger = new Hellog();

const obj = {
  foo: {
    foo: {
      foo: {
        foo: {
          foo: 'bar',
        },
      },
    },
  },
};

logger.log(obj);
logger.success('Hello world !');
logger.debug(obj);
logger.error(new Error('test'));
logger.warn(new Error('test'));

image

Transport and options

Console

This is the transport used by default. It outputs messages withing the console, enriched with some information and colors.

const { Hellog, transports } = require('hellog');

const transport = new transports.Console();

const logger = new Hellog({
  transports: [transport],
});

File

A transport for outputting messages within a file.

const { Hellog, transports } = require('hellog');

const transport = new transports.FileTransport();

const logger = new Hellog({
  transports: [transport],
});

It ouptus JSON objects by default:

{"timestamp":"2021-12-26T15:14:13.668Z","message":"{\n  some: 'bar',\n  foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }\n}","level":"info"}
{"timestamp":"2021-12-26T15:14:13.669Z","message":"{\n  some: 'bar',\n  foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }\n}","level":"success"}
{"timestamp":"2021-12-26T15:14:13.669Z","message":"{\n  some: 'bar',\n  foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }\n}","level":"debug"}
{"timestamp":"2021-12-26T15:14:13.669Z","message":"Error: test\n    at Object.<anonymous> (/Users/escape/Code/hellog/tester.js:19:14)\n    at Module._compile (node:internal/modules/cjs/loader:1101:14)\n    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)\n    at Module.load (node:internal/modules/cjs/loader:981:32)\n    at Function.Module._load (node:internal/modules/cjs/loader:822:12)\n    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)\n    at node:internal/main/run_main_module:17:47","level":"error"}
{"timestamp":"2021-12-26T15:14:13.669Z","message":"Error: test\n    at Object.<anonymous> (/Users/escape/Code/hellog/tester.js:20:13)\n    at Module._compile (node:internal/modules/cjs/loader:1101:14)\n    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)\n    at Module.load (node:internal/modules/cjs/loader:981:32)\n    at Function.Module._load (node:internal/modules/cjs/loader:822:12)\n    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)\n    at node:internal/main/run_main_module:17:47","level":"warning"}

It can also output strings:

const { Hellog, transports } = require('hellog');

const fileTransport = new transports.FileTransport({
  outType: 'string',
  filename: '/path/to/your/log/file', // defaults to "output.log"
});

const logger = new Hellog({
  transports: [fileTransport],
});
2021-12-26 15:20:42 | [info]    | {
2021-12-26 15:20:42 | [info]    |   some: 'bar',
2021-12-26 15:20:42 | [info]    |   foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }
2021-12-26 15:20:42 | [info]    | }
2021-12-26 15:20:42 | [success] | {
2021-12-26 15:20:42 | [success] |   some: 'bar',
2021-12-26 15:20:42 | [success] |   foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }
2021-12-26 15:20:42 | [success] | }
2021-12-26 15:20:42 | [debug]   | {
2021-12-26 15:20:42 | [debug]   |   some: 'bar',
2021-12-26 15:20:42 | [debug]   |   foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }
2021-12-26 15:20:42 | [debug]   | }
2021-12-26 15:20:42 | [error]   | Error: test
2021-12-26 15:20:42 | [error]   |     at Object.<anonymous> (/Users/escape/Code/hellog/tester.js:21:14)
2021-12-26 15:20:42 | [error]   |     at Module._compile (node:internal/modules/cjs/loader:1101:14)
2021-12-26 15:20:42 | [error]   |     at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
2021-12-26 15:20:42 | [error]   |     at Module.load (node:internal/modules/cjs/loader:981:32)
2021-12-26 15:20:42 | [error]   |     at Function.Module._load (node:internal/modules/cjs/loader:822:12)
2021-12-26 15:20:42 | [error]   |     at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
2021-12-26 15:20:42 | [error]   |     at node:internal/main/run_main_module:17:47
2021-12-26 15:20:42 | [warning] | Error: test
2021-12-26 15:20:42 | [warning] |     at Object.<anonymous> (/Users/escape/Code/hellog/tester.js:22:13)
2021-12-26 15:20:42 | [warning] |     at Module._compile (node:internal/modules/cjs/loader:1101:14)
2021-12-26 15:20:42 | [warning] |     at Object.Module._extensions..js (node:internal/modules/cjs/loader:1153:10)
2021-12-26 15:20:42 | [warning] |     at Module.load (node:internal/modules/cjs/loader:981:32)
2021-12-26 15:20:42 | [warning] |     at Function.Module._load (node:internal/modules/cjs/loader:822:12)
2021-12-26 15:20:42 | [warning] |     at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
2021-12-26 15:20:42 | [warning] |     at node:internal/main/run_main_module:17:47

Options

outType: 'json' | 'string'

Defaults to "json", the type of output of the file transport

filename: string

Defaults to "output.log", the path to the file where logs are written. Can absolute or relative. However, we recommend using absolute paths.

Formatters

Every transport takes a formatters parameter in it's options.

const csle = new transports.Console<ExtendedContext>({
  // These are the default formatters, and the logger will default to this list
  formatters: [
    formatters.addLevel(true),
    formatters.addTimestamp(),
    formatters.colorize(),
  ],
});

const logger = new Hellog<ExtendedContext>({
  transports: [csle],
});

logger.suceess('I hve been formatted !');

Available formatters

addTimestamp

Add the current date at the beginning of the line.

2021-12-26 15:20:42 | {
2021-12-26 15:20:42 |   some: 'bar',
2021-12-26 15:20:42 |   foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }
2021-12-26 15:20:42 | }

addLevel(bold: boolean = true)

Add the log level at the beginning of the line. It can be displayed in bold or not.

[info] | {
[info] |   some: 'bar',
[info] |   foo: { nested: { bar: 'test', test: 'bar', hello: 2 } }
[info] | }

colorize

Appends colorizing escape characters at the beginning and the end of the formatted string, depending on the log level

image

Custom formatters

You can implement and add custom formatters, as long as they match the Formatter signature.

type Formatter<T extends PreparedMessage = PreparedMessage> = (
  current: string,
  context: T
) => string;

Example: A simple formatter that will insert a hello message at the beginning of the log

const exampleFormatter = (current: string, context: PreparedMessage) {
  return "hello " + current
}

Before extending PreparedMessage in your formatter declaration, you should read the Extensibility section

Extensibility

The logging functionalities of Hellog are designed to be extensible. You can add personalized behaviour to the following elements of the logger:

Hellog is also designed to guarantee type-safety even with your custom implementations !

To better understand where you can perform custom logic, let's quickly review the lifecycle of a message within the logger.

The logger is called

new Hellog().log({ foo: bar }, 'Hello world');

Context elaboration

Presentation

The very first thing done by Hellog is converting these inputs into a string.

We use util.format to do so, the same function used in the global console. That is the reason why Hellog can offer a console-like API.

Once we have a string, a context object is created. At this point, the type of the context object is PreparedMessage.

{
  timestamp: Date,
  message: string
  level: LogLevel
}

Then, for each transport, it's log method is called. This method is guaranteed to exist, because it is on the BaseTransport abstract class, extended by all transports classes.

Here the default implementation of the log method on the BaseTransport class. Let's break this down.

export abstract class BaseTransport<
  T extends PreparedMessage = PreparedMessage
> {
  //...

  log(log: T) {
    const messages = this.prepareTransport(log);
    const fMessages = messages.map((message) => this.format(message));
    fMessages.forEach((fMessage) => this.transport(fMessage));
  }

  //...
}

Improve context elaboration

If you need to add additional data into the PreparedMessage object being passed along this cycle, you can using the additionalContext when instanciating the Hellog object.

You can even do it with type safety and completion !

import { Hellog } from './logger';

interface ExtendedContext extends PreparedMessage {
  id: number;
}

const displayId: Formatter<ExtendedContext> = (
  current: string,
  context: ExtendedContext
) => {
  return `${context.id} | ${current}`;
};

const csle = new transports.Console<ExtendedContext>({
  formatters: [
    formatters.addLevel(true),
    displayId,
    formatters.addTimestamp(),
    formatters.colorize(),
  ],
});

const logger = new Hellog<ExtendedContext>({
  transports: [csle],
  additionalContext: (message) => ({
    ...message,
    id: Math.floor(Math.random() * 1000),
  }),
});

logger.error(new Error('test'));

Transport

Preparation

In Hellog, messages are formatted and displayed on a per-line basis. Meaning here that if your log spans on mutliple lines, it is breaked into a list of lines on the "\n" character.

Each one of these lines will be formatted during the rest of the execution.

This behaviour can be overriden when writing your own transport class. For example, the File transport is implemented as follows:

export class FileTransport<T extends PreparedMessage = PreparedMessage> {
  //...

  prepareTransport(log: T): T[] {
    if (this.outType === 'json') {
      return [log];
    } else {
      return log.message.split('\n').map((message) => ({ ...log, message }));
    }
  }

  //...
}

Here, we don't want to break line when outputting JSON message, this is left to any system that will monitor this and read the log file.

Each input in the resulting list is passed to the format method of the transport.

Formatting

This section is about extending the formatting logic. See here for Custom Formatters

Formatting is performed on a per-line basis. Each transport is instanciated with a list of formatters, that extends the Formatter type.

export type Formatter<T extends PreparedMessage = PreparedMessage> = (
  current: string,
  context: T
) => string;

For each line, every formatters are called in the order defined by the list provided during class instanciation. By default the base transport uses three standard formats (provided by the library itself).

Here is the default implementation of the format step:

export abstract class BaseTransport<
  T extends PreparedMessage = PreparedMessage
> {
  formatters: Formatter[] = [addLevel(), addTimestamp(), colorize()];

  //...

  format(preparedLine: T): string {
    let formattedMessage = preparedLine.message;
    for (const formatter of this.formatters) {
      formattedMessage = formatter(formattedMessage, preparedLine);
    }
    return formattedMessage;
  }

  //...
}

You can override this behaviour when doing your own transport class.

Transportation

Once the log is nothing more than a list of string, the transport method of the transport is called for each resulting string.

Very various thing happen at this point. by default, a simple console.log is performed here. The FileTransport writes into a fs.WriteStream. You can implement a transport where the lines are sent over HTTP. Here are some examples.

You might also like...

Hourly weather card for Home Assistant. Visualize upcoming weather conditions as a colored horizontal bar.

Hourly weather card for Home Assistant. Visualize upcoming weather conditions as a colored horizontal bar.

Hourly Weather Card by @decompil3d An hourly weather card for Home Assistant. Visualize upcoming weather conditions as a colored horizontal bar. Can y

Dec 29, 2022

Download all Moodle files with one click. This is a Chrome extension built to save time and effort from downloading files manually one by one!

Download all Moodle files with one click. This is a Chrome extension built to save time and effort from downloading files manually one by one!

Moodle Downloader Extension Moodle downloader extension for Chrome. The extension is tested with both the TUM moodle and the official moodle demo. Not

Nov 15, 2022

Send all tokens from one wallet to another quickly.

Send all tokens from one wallet to another quickly.

Drain Send all tokens from one wallet to another quickly. Whether getting hacked or starting fresh, Drain makes sure you get every last wei. Live Depl

Dec 22, 2022

Trusted timestamps that you can physically include in photos, videos and live streams using QR codes and audible data signals.

Trusted timestamps that you can physically include in photos, videos and live streams using QR codes and audible data signals.

QR Date This is the reference implementation for the first version of QR Date, a signed timestamp inside a QR code that you can use to verify the date

Oct 5, 2022

This plugin can generate timestamps for video, audio and Bilibili video, it takes you to the corresponding video/audio position when clicked.

logseq-plugin-media-ts 本插件能够生成视频、音频以及 B 站视频的时间戳,点击时间戳后会跳转到对应的音视频位置。 This plugin can generate timestamps for video, audio and Bilibili video, it takes

Jan 3, 2023

This plugin allows side-by-side notetaking with videos. Annotate your notes with timestamps to directly control the video and remember where each note comes from.

Obsidian Timestamp Notes Use Case Hello Obsidian users! Like all of you, I love using Obsidian for taking notes. My usual workflow is a video in my br

Jan 2, 2023

Smart Time Ago is a little jQuery library to update the relative timestamps in your document.

Smart Time Ago Smart Time Ago is a little jQuery library to update the relative timestamps in your document intelligently. (e.g "3 hours ago"). It's o

Dec 6, 2022

🪐 The IPFS gateway for NFT.Storage is not "another gateway", but a caching layer for NFTs that sits on top of existing IPFS public gateways.

nftstorage.link The IPFS gateway for nft.storage is not "another gateway", but a caching layer for NFT’s that sits on top of existing IPFS public gate

Dec 19, 2022
Owner
Maxence Lecanu
Maxence Lecanu
Colorconsole provides an interesting way to display colored info, success, warning and error messages on the developer console in your browser

ColorConsole NPM Package Colorconsole provides an interesting way to display colored info, success, warning and error messages on the developer consol

Hasin Hayder 17 Sep 19, 2022
Tiny JavaScript library (1kB) by CurrencyRate.today, providing simple way and advanced number, money and currency formatting and removes all formatting/cruft and returns the raw float value.

Zero dependency tiny JavaScript library (1kB bytes) by CurrencyRate.today, providing simple way and advanced number, money and currency formatting and removes all formatting/cruft and returns the raw float value.

Yurii De 11 Nov 8, 2022
Base provides advanced Promise Queue Manager, Custom Console Logger and other utilities.

Base Base provides frequently used functionality like cutome logger, response helper, Custom Promise and Instance composer. These are used in almost a

PLG Works 14 Jun 14, 2022
A NodeJS Console Logger with superpowers

LogFlake LogFlake is a NodeJS console logger with superpowers. It has the same API as the usual Console but with beautified output, a message header w

Felippe Regazio 57 Oct 9, 2022
An elegant console logger.

kons An elegant console logger. Features Tiny (Minified + Gzipped ≈ 0.1kB). Beautiful. Easy to use. Customizable. TypeScript type declarations include

ʀᴀʏ 6 Aug 13, 2022
A string of four operations of the library, can solve the js digital calculation accuracy of scientific notation and formatting problems, support for thousands of decimal point formatting output operations

A string of four operations of the library, can solve the js digital calculation accuracy of scientific notation and formatting problems, support for thousands of decimal point formatting output operations

null 10 Apr 6, 2022
Console log to terminal

Termlog Console log to terminal What it does termlog send the browser console log to your terminal It also comes with a nodejs REPL so you can do some

Quang Ngoc 16 Jan 21, 2022
Style templates for console.log

sttyl Style templates for console.log. Usage This module is primarily intended for Deno and browsers. import { sttyl } from "https://deno.land/x/sttyl

Wayne Bloss 8 Dec 27, 2022
This is a toy, it is an enhanced version of console.log

uu-console Hi, this is a toy. When you use console.log to print some logs, this can help you do some useless things. What is uu-console Support built-

KK Liu 2 Sep 25, 2022