add FileFollower for tail -f functionality

This commit is contained in:
Sami Samhuri 2011-05-30 01:12:31 -07:00
parent 5295fe8f20
commit 1652d2a782
2 changed files with 88 additions and 4 deletions

View file

@ -3,6 +3,7 @@
var fs = require('fs')
, ArrayExt = require('./array-ext')
, FileFollower = require('./file-follower')
, LineEmitter = require('./line-emitter')
, ObjectExt = require('./object-ext')
, constants = require('constants')
@ -12,6 +13,7 @@ var fs = require('fs')
FileExt =
{ eachLine: eachLine
, exists: exists
, follow: follow
, grep: grep
, home: home
, readLines: readLines
@ -58,14 +60,22 @@ function eachLine(f, optionsOrLineFn, endFn) {
function exists(f) {
try {
fs.statSync(f)
return true
fs.statSync(f);
return true;
} catch (e) {
if (e.errno === constants.ENOENT) return false
throw e
if (e.errno === constants.ENOENT) return false;
throw e;
}
}
function follow(f, lineFn) {
var ff = new FileFollower(f);
ff.on('line', lineFn);
return {
stop: ff.stopFollowing.bind(ff)
};
}
function grep(regex, f, callback) {
if (!callback) throw new Error('grep requires a callback');
var results = [];

74
lib/file-follower.js Normal file
View file

@ -0,0 +1,74 @@
// batteries
// Copyright 2010 - 2011 Sami Samhuri <sami@samhuri.net>
var fs = require('fs')
, util = require('util')
, EventEmitter = require('events').EventEmitter
, FileExt = require('./file-ext')
;
module.exports = FileFollower;
// TODO: option to act like tail and only show the last N lines, N >= 0
function FileFollower(file, options) {
options = options || {};
var self = this;
this.file = file;
this.currSize = fs.statSync(file).size;
this.prevSize = this.currSize;
this.interval = options.interval || 1000;
FileExt.eachLine(file,
{ line: function(line) {
self.emit('line', line);
}
, end: function() {
self.startFollowing();
}
});
}
util.inherits(FileFollower, EventEmitter);
FileFollower.prototype.startFollowing = function() {
if (this._interval) {
console.warn('already following');
return;
}
this.buffer = '';
this.fd = fs.openSync(this.file, 'r');
this._interval = setInterval(this.checkForLine.bind(this), this.interval);
};
FileFollower.prototype.stopFollowing = function() {
if (!this._interval) {
console.warn('not following');
return;
}
delete this.buffer;
clearInterval(this._interval);
delete this._interval;
fs.closeSync(this.fd);
delete this.fd;
};
FileFollower.prototype.checkForLine = function() {
this.currSize = fs.statSync(this.file).size;
if (this.currSize > this.prevSize) {
var n = this.currSize - this.prevSize
, buf = new Buffer(n + 1)
, self = this
;
fs.read(this.fd, buf, 0, n, this.prevSize, function(err, bytesRead, buffer) {
if (err) {
self.emit('error', err);
return;
}
self.buffer += buf.slice(0, bytesRead);
self.prevSize += bytesRead;
var i;
while ((i = self.buffer.indexOf('\n')) !== -1) {
self.emit('line', self.buffer.slice(0, i));
self.buffer = self.buffer.slice(i + 1);
}
});
}
};