npm package: @keeex/log
Description: Basic logging facilities
Author: KeeeX SAS
Latest version: 1.7.2

# Common logging facilities

## Description

This library provides logging facilities to be used throughout multiple projects/libraries.
The following features are supported:

- format final log output (defaults to `console.log()` and the like) with timestamp, log level, and optional tag
- fine control of log levels
- tag together a group of logging calls, to disable/enable them individually
- add a handler to grab all log output
- override (to some extent) the default `console` loggers
- temporarily buffering stdout

## Usage

Import the library, and either use the default logger or create tagged ones.

```javascript
import * as logFacility from "@keeex/log";

logFacility.catchConsole();

logFacility.logger.info("Info string");
console.info("Another info string");

const logger = logFacility.createLogger({tag: "someFunc"});
logger.info("Tagged message");
```

Both the default `logger` and the output of `createLogger()` returns an object with the following functions:

- `debug`
- `error`
- `info`
- `silly`
- `warn`

These functions correspond to the various log levels.
All loggers can be customized by either changing the shared configuration, or on each logger individually when they are created.

### Basic logger usage

The top-level logger (the one in the `logger` export) is also available as direct exports of the functions `debug`, `info`, etc. from the package.

## Formatting

The output messages will be prefixed, depending on the configuration, with the current timestamp, log level, and if applicable logging tag.

## Configuration

Some logging properties can be changed by calling `setup()`:

```javascript
import * as logFacility from "@keeex/log";

logFacility.setup({
  elapsedTime: false,
  noInfoCopy: false,
  outputMode: OutputMode.levelTs,
  rawLoggers: {},
});
```

- `elapsedTime` appends the elapsed time since the module was loaded to the console output
- `noInfoCopy` prevent the duplication of error and warning message on the info output
- `outputMode` can be set to not use the timestamp, or the log level in the output
- `rawLoggers` can be used to change the final output function (defaults to `console.log()` and `console.error()`)

If these properties are set using the `setup()` function, they apply to _all_ loggers that does not override them.
These properties are also available when creating a custom logger, alongside the `tag` property.

## Log level

The default log level is `info`, which output info, warning, and error messages.
The available log levels are, in order, `silly`, `debug`, `info`, `warning`, `error`.

There are two ways to set the log level.
By calling `setLogLevel(logLevel: LogLevel)`, you can quickly determine the lowest level of log; all higher levels will be enabled too.
You can also call `tuneLogLevel()` to toggle each log level individually.

Messages that are in a disabled log level will not be processed.

## Tags

Custom loggers (created with `createLogger()`) can be associated to a tag.
Tags are simple string that can include `:` as a delimiter, although this is not enforced.

By default, all tags are visible.
If at least one tag is explicitely enabled, then only enabled tags are visible.
A tag is considered enabled if it _starts_ with an enabled string.
For example, the tag "debug:manager" would be enabled with either "debug:" or "debug:manager", or even "debug:man" but not by "debug:exp".

Enabling and disabling tags is done by calling `enableTag()` and `disableTag()`.
Removing all enabled tags is done using `clearTag()`.
There is no particular consistency checks when enabling tags, so overlapping values will be ignored.

Passing `numbered: true` as a property when creating a custom logger will append `:<number>` at the end of the tag.
This can be used to distinguish between multiple instances of the same class, for example.

When a tag is used but should be ignored, it will output a one-time warning message indicating that this tag is disabled.

## Log handlers

It is possible to intercept all log events (as long as they are not disabled) by adding an event handler:

```javascript
import * as fs from "node:fs";
import * as logFacility from "@keeex/log";

const handler = (logLevel, message) => {
  if (logLevel === LogLevel.error) fs.appendSync("error.log", `${message}\n`);
};

logFacility.addLogHandler(handler);
```

The handlers can take more parameters to perform advanced filtering based on the tag or raw message.

## Buffering stdout

If an application might use stdout for useful purpose, it can avoid tainting it in two ways:

- Call `protectStdout()` as soon as possible.
  Afterward, all logging (using the default log facilities) is redirected to stderr
- Call `bufferStdout()` as soon as possible.
  This will temporarily buffer all output to stdout.
  When the program have determined whether it wants to protect stdout or not, it can call `protectStdout()` or `unbufferStdout()`.
  The buffered content will be output as expected.