log4js2

The fast, flexible logger for JavaScript

View project on GitHub

log4js2 is a fast and lightweight logging library that enables logging flexibility within JavaScript applications, similar to Apache's Log4j2 library. It can also serve as a drop-in replacement for log4js, since the namespace and functions are mostly similar.

Installation & Building

> npm i -S @log4js2/core

Configuration

Configure log4js using the configure() method. This must be the first thing you do. Otherwise, the first log you commit will not allow updates from this function.

import {configure, LogLevel} from '@log4js2/core';

configure({
    level: LogLevel.INFO
    layout: '%d [%p] %c %M:%line:%column - %m %ex',
    appenders: ['Console'],
    loggers: [{
        tag: 'App',
        logLevel : LogLevel.INFO
    }]
});

Virtual Console

This library utilizes a virtual console to intercept console logs from other libraries/scripts. This is intended to allow usage of this library without having to replace all console.log commands within your code - or to intercept logs from third-party libraries to input into your own log stream. Make sure log4js2 is loaded at the top of the page to ensure that all logs are caught.

import * from '@log4js2/core';

console.log('console log');

// outputs: 08-30-2018 12:38:00 [INFO] main - console log

To disable this feature, set the virtualConsole property to false when configuring log4js2.

configure({
    // ...
    virtualConsole: false
});

Usage

Simple Logging

To log, use getLogger to get a logger, and then use (in increasing granularity) fatal, error, warn, info, debug, or trace to output logs. Because of how console is structured in JavaScript, debug and trace will log at the same level, as well as error and fatal.

const logger = getLogger('app');

logger.info('something happened!');
logger.error('something bad happened!');

Variables

Variables can be inputted into your log statement by using curly braces {}, and will be replaced with the supplied arguments at the corresponding index. So, the first curly brace will take the first argument after the message string, the second curly brace the second argument, etc.

logger.info('The user email is: {}', email);

Errors

If specified in the pattern layout, by adding an error to your list of arguments, the stacktrace will be outputted to the logs. Your error must be the last argument, and not consumed by a {}, otherwise it will not display

try {
    // execute something
} catch (e) {

    // stacktrace displays
    logger.error('There was an error: {}', e.message, e);

    // stacktrace does not display
    logger.error('There was an error: {}', e);

}

Markers

Makers are useful to either "mark" a log event for specific filtering, or add useful information to your logs. It is a good idea not to generate markers dynamically, but declare them beforehand as constants.

const sqlMarker = Marker.getMarker('sql');
const updateMarker = Marker.getMarker('update').addParents(sqlMarker);

logger.info(updateMarker, 'Updating the database');

Levels

The log4js2 LogLevel enum specifies the log levels (in decreasing severity) FATAL, ERROR, WARN, INFO, DEBUG, and TRACE - with an inclusion of ALL and NONE for configuration purposes.

Specifying Levels

You can specify multiple log levels, in order of precedence, at the logger, appender, and global levels. This allows logs to be, for example, suppressed in a browser console while being passed to another appender to be pushed to an HTTP endpoint. By using ALL, you can allow any and all level logs to be outputted, and by using NONE, restrict any output.

configure({
    level: LogLevel.WARN, // default
    appenders: [{
        appender: 'RollingFile',
        level: LogLevel.INFO // appender
    }],
    loggers: [{
        tag: 'DAO',
        level: LogLevel.ALL // logger
    }]
});

Pattern Layout

Specifying a Layout

Like log levels, you can specify multiple pattern layouts, in order of precedence, at the logger, appender, and global levels. Pattern layouts are specified using tags, which describe a particular log event attribute (see below).

configure({
    layout: '%d [%p] %c - %m', // default
    appenders: [{
        appender: 'Console',
        layout: '%d{ISO8601} [%p] - %m %exception' // appender
    }],
    loggers: [{
        tag: 'App',
        layout: '%c.%method (%line:%column) %m %ex' // logger
    }]
});

If a tag takes parameters, those are specified within the proceeding curly brackets {}. Do not add a space between the tag and the parameter(s) container - so %d {DEFAULT} is invalid, whereas %d{DEFAULT} is correct.

Tags

Logger

%c %logger

Outputs the name of the logger that pushed the message. If no logger is specified, the main logger is used. This tag takes no parameters.

Date

%d %date

Outputs the current date the log was issued at. You can specify the date format using one of the named date formats (below), or by supplying your own date format string. Using %d or %date without a format will behave like %d{DEFAULT}

Name Format
DEFAULT yyyy-MM-dd HH:mm:ss,S
ABSOLUTE HH:mm:ss,S
COMPACT yyyyMMddHHmmssS
DATE dd MMM yyyy HH:mm:ss,S
ISO8601 yyyy-MM-ddTHH:mm:ss,S
ISO8601_BASIC yyyyMMddTHHmmss,S

Exception

%ex %exception %throwable

Outputs the stack trace for a thrown error (if present). If no error is present, this tag will not render.

 logger.error('an error occurred!', err); 

File

%F %file

Outputs the file name and location that emitted the log.

Map

%K %map %MAP

Outputs a map to a readable format. The value must be of type object with key-value pairs, or type Map. There can also be no corresponding curly brace {} in the log message, otherwise the value will not output.

logger.info('Output: ', { a: 1, foo: 'bar' });

// [INFO] main - {{a,1},{foo,bar}}

Line

%L %line

Outputs the line number the log was emitted from.

Column

%column

Outputs the column number the log was emitted from.

Log Message

%m %msg %message

The log message, with all curly brace {} arguments substituted. Your pattern layout must have this tag in order to display the log message, which is not emitted regardless of layout.

Marker

%marker %markerSimpleName

Outputs the marker for the log event, or nothing if absent. The %marker tag will output the marker and its parents, while %markerSimpleName will output only the marker name.

Method

%M %method

The method (function) that called the logger. If the function is named, the name of the method will be shown, otherwise anonymous will be outputted. This tag is expensive in its execution, so it's a good idea not to use it in a production environment. Also, this tag is unreliable when using 'use strict'.

Line Separator

%n

Outputs a simple line separator \n within the logs. All logs already terminate with a new line separator, so this is only useful if you want to split your log over multiple lines.

Log Level

%p %level

The log level for the given log event. The level can be FATAL, ERROR, WARN, INFO, DEBUG, or TRACE.

Relative

%r %relative

The relative time of a given log to the creation of its logger, in milliseconds. So, for example, if the logger was created at 5:35:10 PM, and the log was generated at 5:36:00 PM, the outputted relative value would be 50000.

Sequence

%sn %sequenceNumber

The sequence number of the log for the given logger. This number starts at 1 and increments for every subsequent log entry.

Appenders

Configuring

By specifying appenders by name, you can control which loggers log to which appender. This is useful when you want to isolate logs to a specific output file, or prevent certain logs from displaying in the console.

configure({
    appenders: ['Console', {
        appender: 'File',
        fileName: 'app.log'
    }, {
        name: 'SQLAppender',
        appender: 'File',
        fileName: 'sql.log'
    }],
    loggers: [{
        tag: 'sql',
        appenders: ['SQLAppender'] // will ONLY log to appender named 'SQLAppender'
    }]
});

Available Appenders

Console

This is the default appender that outputs logs to the system out. If no appender is specified in the configuration, the console appender will be used.

// you can use just the appender name
// if you don't want to specify any configuration
configure({
    appenders: ['Console']
});

File

> npm i -D @log4js2/file-appender

This is a simple file appender that outputs logs to a single file. There is no consideration for file size, so it is wise to only utilize this appender when the process execution terminates after a set time period (e.g. not a http server).

configure({
    layout : '%d [%p] %c %M:%line:%column - %m %ex',
    appenders: [{
        appender: 'File',
        config: {
            fileName: './logs/app.log'
        }
    }]
});

Rolling File

> npm i -D @log4js2/file-appender

The rolling file appender allows for logs to be appended to a log file, to be split and backed up once the file reaches a certain size. The filePattern attribute can contain either %d tag for the log file creation date, or a %i tag to specify the increment.

You can control the size (in megabytes) by setting the maxSize attribute, or of the max number of log file backups to retain by setting maxBackup.

configure({
    layout : '%d [%p] %c %M:%line:%column - %m %ex',
    appenders: [{
        appender: 'RollingFile',
        config: {
            fileName: './logs/rollingfile.log',
            filePattern: './logs/rollingfile.%d{DEFAULT}.log',
            maxBackup: 10,
            maxSize: 10
        }
    }]
});

Custom Appenders

You can create custom log appenders for specific use cases.

@Appender('MyAppender')
export class MyAppender extends LogAppender<MyAppenderConfiguration> {

    public static get appenderName(): string {
        return 'MyAppender';
    }

    public append(logEvent: ILogEvent) {
        if (logEvent.level <= this.getLogLevel()) {

            const message = this.format(logEvent);

            // handle message

        }
    }

}
import { MyAppender } from './myappender.ts';

configure({
    // ...
    appenders: [{
        appender: MyAppender, // can also refer by name ('MyAppender')
        config: {
            // ...
        }
    }]
});

Filters

Filters allow you to add fine-grain control to appenders by filtering what logs can reach them. You can set the onMatch and onMismatch behaviors using ALLOW, DENY, NEUTRAL.

Marker

The marker filter allows you to control which logs are appended by the marker attached to the log. The marker attribute corresponds to the marker name, or a marker's parent name.

configure({
    appenders: [{
        appender: RollingFile,
        filters: [{
            filter: 'Marker',
            config: {
                marker: 'MyMarker'
            }
            onMatch: LogFilterAction.ALLOW,
            onMismatch: LogFilterAction.DENY
        }]
    }]
});

Threshold

Allows you to control what log level the appender accepts.

configure({
    appenders: [{
        appender: SNSAppender,
        filters: [{
            filter: 'Threshold',
            config: {
                level: LogLevel.FATAL
            },
            onMatch: LogFilterAction.ALLOW,
            onMismatch: LogFilterAction.DENY
        }]
    }]
});

Custom

You can create custom filters to handle specific use cases.

@Filter('MyFilter')
export class MyFilter extends LogFilter<MyFilterConfiguration> {

    public isMatch(logEvent?: ILogEvent): boolean {

        // check if match
        // use this.configuration to grab the current configuration

    }

}