Writing Appenders For log4js ============================ Loading appenders ----------------- log4js supports loading appender modules from outside its own code. The [log4js-gelf](http://github.com/nomiddlename/log4js-gelf), [log4js-smtp](http://github.com/nomiddlename/log4js-smtp), and [log4js-hookio](http://github.com/nomiddlename/log4js-hookio) appenders are examples of this. In the configuration for an appender, log4js will first attempt to `require` the module from `./lib/appenders/ + type` within log4js - if that fails, it will `require` just using the type. e.g. log4js.configure({ appenders: { "custom": { type: "log4js-gelf", hostname: "blah", port: 1234 } }, categories: { "default": { level: "debug", appenders: ["custom"] } } }); log4js will first attempt to `require('./appenders/' + log4js-gelf)`, this will fail. It will then attempt `require('log4js-gelf')`, which (assuming you have previously run `npm install log4js-gelf`) will pick up the gelf appender. Writing your own custom appender -------------------------------- This is easiest to explain with an example. Let's assume you want to write a [CouchDB](http://couchdb.apache.org) appender. CouchDB is a document database that you talk to via HTTP and JSON. Our log4js configuration is going to look something like this: log4js.configure({ appenders: { "couch": { type: "log4js-couchdb", url: "http://mycouchhost:5984", db: "logs", layout: { type: "messagePassThrough" } } }, categories: { "default": { level: "debug", appenders: ["couch"] } } }); When processing this configuration, the first thing log4js will do is `require('log4js-couchdb')`. It expects this module to return a function that accepts two arguments module.exports = function(layouts, levels) { ... }; log4js will then call that function, passing in the `layouts` and `levels` sub-modules in case your appender might need to use them. Layouts contains functions which will format a log event as a string in various different ways. Levels contains the definitions of the log levels used by log4js - you might need this for mapping log4js levels to external definitions (the GELF appender does this). These are passed in so that appenders do not need to include a hard dependency on log4js (see below), and so that log4js does not need to expose these modules to the public API. The module function will only be called once per call to `log4js.configure`, even if there are multiple appenders of that type defined. The module function should return another function, a configuration function, which will be called for each appender of that type defined in the config. That function should return an appender instance. For our CouchDB example, the calling process is roughly like this: couchDbModule = require('log4js-couchdb'); appenderMaker = couchDbModule(layouts, levels); appender = appenderMaker({ type: "log4js-couchdb", url: "http://mycouchhost:5984", db: "logs", layout: { type: "messagePassThrough" } }, appenderByName) Note that in addition to our couchdb appender config, the appenderMaker function gets an extra argument: `appenderByName`, a function which returns an appender when passed its name. This is used by appenders that wrap other appenders. The `logLevelFilter` is an example of this use. The `layout` portion of the config can be passed directly to `layouts.layout(config.layout)` to generate a layout function. The appender function returned after processing your config should just take one argument: a log event. This function will be called for every log event that should be handled by your appender. In our case, with the config above, every log event of DEBUG level and above will be sent to our appender. Dependencies ------------ You should declare which version of log4js your appender works with by including a "peerDependencies" section in your package.json. e.g. { "name": "my-cool-appender", "version": "0.0.1", ... "peerDependencies": { "log4js": "0.7.x" } } For more details on peer dependencies, see [this blog post](http://blog.nodejs.org/2013/02/07/peer-dependencies/).