From 45b1eebe6a4a4cf2487344e6467c92a18d972bb2 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 9 Jun 2015 17:59:42 -0700 Subject: [PATCH] [fix] Get `tailFile` function working on latest/all node versions --- lib/winston/common.js | 119 +++++++++++++++++++++++++++++------------- 1 file changed, 84 insertions(+), 35 deletions(-) diff --git a/lib/winston/common.js b/lib/winston/common.js index e201c697c..7351b11cc 100644 --- a/lib/winston/common.js +++ b/lib/winston/common.js @@ -10,6 +10,8 @@ var util = require('util'), crypto = require('crypto'), cycle = require('cycle'), fs = require('fs'), + StringDecoder = require('string_decoder').StringDecoder, + Stream = require('stream').Stream, config = require('./config'); // @@ -337,60 +339,107 @@ exports.serialize = function (obj, key) { return msg; }; - // // ### function tailFile (options, callback) // #### @options {Object} Options for tail. // #### @callback {function} Callback to execute on every line. // `tail -f` a file. Options must include file. // -exports.tailFile = function tail(options, callback) { - var stream = fs.createReadStream(options.file, { encoding: 'utf8' }), - buff = '', - destroy, - row = 0; - - destroy = stream.destroy.bind(stream); - stream.destroy = function () {}; +exports.tailFile = function(options, callback) { + var buffer = new Buffer(64 * 1024) + , decode = new StringDecoder('utf8') + , stream = new Stream + , buff = '' + , pos = 0 + , row = 0; if (options.start === -1) { delete options.start; } - stream.on('data', function (data) { - var data = (buff + data).split(/\n+/), - l = data.length - 1, - i = 0; - - for (; i < l; i++) { - if (options.start == null || row > options.start) { - callback(null, data[i]); + stream.readable = true; + stream.destroy = function() { + stream.destroyed = true; + stream.emit('end'); + stream.emit('close'); + }; + + fs.open(options.file, 'a+', 0644, function(err, fd) { + if (err) { + if (!callback) { + stream.emit('error', err); + } else { + callback(err); } - row++; + stream.destroy(); + return; } - buff = data[l]; - }); + (function read() { + if (stream.destroyed) { + fs.close(fd); + return; + } - stream.on('error', function (err) { - callback(err); - destroy(); - }); + return fs.read(fd, buffer, 0, buffer.length, pos, function(err, bytes) { + if (err) { + if (!callback) { + stream.emit('error', err); + } else { + callback(err); + } + stream.destroy(); + return; + } - stream.on('end', function () { - if (buff) { - stream.emit('line', buff); - buff = ''; - } + if (!bytes) { + if (buff) { + if (options.start == null || row > options.start) { + if (!callback) { + stream.emit('line', buff); + } else { + callback(null, buff); + } + } + row++; + buff = ''; + } + return setTimeout(read, 1000); + } + + var data = decode.write(buffer.slice(0, bytes)); + + if (!callback) { + stream.emit('data', data); + } + + var data = (buff + data).split(/\n+/) + , l = data.length - 1 + , i = 0; + + for (; i < l; i++) { + if (options.start == null || row > options.start) { + if (!callback) { + stream.emit('line', data[i]); + } else { + callback(null, data[i]); + } + } + row++; + } + + buff = data[l]; + + pos += bytes; - resume(); + return read(); + }); + })(); }); - function resume() { - setTimeout(function () { - stream.resume(); - }, 1000); + if (!callback) { + return stream; } - return destroy; + return stream.destroy; };