log4js-node

A port of log4js to node.js

View the Project on GitHub

Writing Appenders for Log4js

Log4js can load appenders from outside its core set. To add a custom appender, the easiest way is to make it a stand-alone module and publish to npm. You can also load appenders from your own application, but they must be defined in a module.

Loading mechanism

When log4js parses your configuration, it loops through the defined appenders. For each one, it will require the appender initially using the type value prepended with ‘./appenders’ as the module identifier - this is to try loading from the core appenders first. If that fails (the module could not be found in the core appenders), then log4js will try to require the module using variations of the type value.

Log4js checks the following places (in this order) for appenders based on the type value:

  1. The core appenders: require('./appenders/' + type)
  2. node_modules: require(type)
  3. relative to the main file of your application: require(path.dirname(require.main.filename) + '/' + type)
  4. relative to the process’ current working directory: require(process.cwd() + '/' + type)

If that fails, an error will be raised.

Appender Modules

An appender module should export a single function called configure. The function should accept the following arguments:

configure should return a function which accepts a logEvent, which is the appender itself. One of the simplest examples is the stdout appender. Let’s run through the code.

Example

// This is the function that generates an appender function
function stdoutAppender(layout, timezoneOffset) {
  // This is the appender function itself
  return (loggingEvent) => {
    process.stdout.write(`${layout(loggingEvent, timezoneOffset)}\n`);
  };
}

// stdout configure doesn't need to use findAppender, or levels
function configure(config, layouts) {
  // the default layout for the appender
  let layout = layouts.colouredLayout;
  // check if there is another layout specified
  if (config.layout) {
    // load the layout
    layout = layouts.layout(config.layout.type, config.layout);
  }
  //create a new appender instance
  return stdoutAppender(layout, config.timezoneOffset);
}

//export the only function needed
exports.configure = configure;

Shutdown functions

It’s a good idea to implement a shutdown function on your appender instances. This function will get called by log4js.shutdown and signals that log4js has been asked to stop logging. Usually this is because of a fatal exception, or the application is being stopped. Your shutdown function should make sure that all asynchronous operations finish, and that any resources are cleaned up. The function must be named shutdown, take one callback argument, and be a property of the appender instance. Let’s add a shutdown function to the stdout appender as an example.

Example (shutdown)

// This is the function that generates an appender function
function stdoutAppender(layout, timezoneOffset) {
  // This is the appender function itself
  const appender = (loggingEvent) => {
    process.stdout.write(`${layout(loggingEvent, timezoneOffset)}\n`);
  };

  // add a shutdown function.
  appender.shutdown = (done) => {
    process.stdout.write('', done);
  };

  return appender;
}

// ... rest of the code as above