Writing to the syslog with Winston

This article was originally published in my blog (affectionately referred to as blargh) on . The original blog no longer exists as I've migrated everything to this wiki.

The original URL of this post was at https://tmont.com/blargh/2013/12/writing-to-the-syslog-with-winston. Hopefully that link redirects back to this page.

Update: this code is now in its own GitHub repository.


Many log aggregators (such as Papertrail or Loggly) have the ability to parse data from the syslog. This is often more convenient than writing to a file, handling rotation of said log file, handling archival of said rotated log file, and a myriad of other annoyances. Plus, many other system programs (ssh, cron, etc.) already log stuff to the syslog, so most things are already aggregated.

I use Winston as my logging library for Node. It works pretty well inasmuch as it lets you log stuff. Winston exposes a Transport object, which is an interface to a logging destination (a file, the console, a webhook, etc.). You can use this to log to the syslog.

Note that there are other modules for Winston that expose a syslog transport, but they all made me nervous, in that they weren't tested, and after glancing through the code, I wasn't convinced they were doing proper socket management. And the last thing I wanted was my logging library to create hundreds of undisposed file descriptors. So I decided to use node-posix and just use the OS to write to the syslog rather than UDP. Plus syslogd doesn't have datagram support enabled by default, so you need to tweak the configuration to make it accept UDP packets, and I don't know enough about syslog to feel confident in messing with the configuration. And also I didn't feel like spending an hour learning about it. Because I'm weak and stupid.

Anyway, here's some sample code to write to the syslog using the OS's POSIX functions.

javascript
var winston = require('winston'),
	util = require('util'),
	posix = require('posix');

function SyslogTransport(options) {
	options = options || {};
	winston.Transport.call(this, options);
	this.id = options.id || process.title;
	this.facility = options.facility || 'local0';
	this.showPid = !!options.showPid;
}

util.inherits(SyslogTransport, winston.Transport);

util._extend(SyslogTransport.prototype, {
	name: 'syslog',
	log: function(level, msg, meta, callback) {
		if (this.silent) {
			callback(null, true);
			return;
		}

		if (level === 'error') {
			level = 'err';
		} else if (level === 'warn') {
			level = 'warning';
		}

		var message = '[' + level + '] ' + msg;
		if (typeof(meta) === 'string') {
			message += ' ' + meta;
		} else if (meta && typeof(meta) === 'object' && Object.keys(meta).length > 0) {
			message += ' ' + util.inspect(meta, false, null, false);
		}

		message = message.replace(/\u001b\[(\d+(;\d+)*)?m/g, '');

		var options = {
			cons: true,
			pid: this.showPid
		};
		posix.openlog(this.id, options, this.facility);
		posix.syslog(level, message);
		posix.closelog();

		callback(null, true);
	}
});

module.exports = SyslogTransport;

And you could use it like so:

javascript
var transport = new SyslogTransport({
	level: 'debug',
	id: 'hello syslog',
	facility: 'user',
	showPid: true
});

var log = new winston.Logger({
	level: 'debug',
	transports: [ transport ]
});

log.info('hello world');

And then if you run tail /var/log/syslog (/var/log/messages on OS X, I think), you should see something like this:

plaintext
Dec 23 12:01:50 mesia hello syslog[17605]: [info] hello world