UNPKG

6.35 kBJavaScriptView Raw
1// Generated by CoffeeScript 2.3.1
2var Tail, environment, events, fs,
3 boundMethodCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } };
4
5events = require("events");
6
7fs = require('fs');
8
9environment = process.env['NODE_ENV'] || 'development';
10
11Tail = class Tail extends events.EventEmitter {
12 readBlock() {
13 var block, stream;
14 boundMethodCheck(this, Tail);
15 if (this.queue.length >= 1) {
16 block = this.queue[0];
17 if (block.end > block.start) {
18 stream = fs.createReadStream(this.filename, {
19 start: block.start,
20 end: block.end - 1,
21 encoding: this.encoding
22 });
23 stream.on('error', (error) => {
24 if (this.logger) {
25 this.logger.error(`Tail error: ${error}`);
26 }
27 return this.emit('error', error);
28 });
29 stream.on('end', () => {
30 var x;
31 x = this.queue.shift();
32 if (this.queue.length > 0) {
33 this.internalDispatcher.emit("next");
34 }
35 if (this.flushAtEOF && this.buffer.length > 0) {
36 this.emit("line", this.buffer);
37 return this.buffer = '';
38 }
39 });
40 return stream.on('data', (data) => {
41 var chunk, i, len, parts, results;
42 if (this.separator === null) {
43 return this.emit("line", data);
44 } else {
45 this.buffer += data;
46 parts = this.buffer.split(this.separator);
47 this.buffer = parts.pop();
48 results = [];
49 for (i = 0, len = parts.length; i < len; i++) {
50 chunk = parts[i];
51 results.push(this.emit("line", chunk));
52 }
53 return results;
54 }
55 });
56 }
57 }
58 }
59
60 constructor(filename, options = {}) {
61 var pos;
62 super(filename, options);
63 this.readBlock = this.readBlock.bind(this);
64 this.filename = filename;
65 ({separator: this.separator = /[\r]{0,1}\n/, fsWatchOptions: this.fsWatchOptions = {}, fromBeginning: this.fromBeginning = false, follow: this.follow = true, logger: this.logger, useWatchFile: this.useWatchFile = false, flushAtEOF: this.flushAtEOF = false, encoding: this.encoding = "utf-8"} = options);
66 if (this.logger) {
67 this.logger.info("Tail starting...");
68 this.logger.info(`filename: ${this.filename}`);
69 this.logger.info(`encoding: ${this.encoding}`);
70 }
71 this.buffer = '';
72 this.internalDispatcher = new events.EventEmitter();
73 this.queue = [];
74 this.isWatching = false;
75 this.internalDispatcher.on('next', () => {
76 return this.readBlock();
77 });
78 if (this.fromBeginning) {
79 pos = 0;
80 }
81 this.watch(pos);
82 }
83
84 watch(pos) {
85 var err, stats;
86 if (this.isWatching) {
87 return;
88 }
89 this.isWatching = true;
90 try {
91 stats = fs.statSync(this.filename);
92 } catch (error1) {
93 err = error1;
94 if (this.logger) {
95 this.logger.error(`watch for ${this.filename} failed: ${err}`);
96 }
97 this.emit("error", `watch for ${this.filename} failed: ${err}`);
98 return;
99 }
100 this.pos = pos != null ? pos : stats.size;
101 if (this.logger) {
102 this.logger.info(`filesystem.watch present? ${fs.watch !== void 0}`);
103 this.logger.info(`useWatchFile: ${this.useWatchFile}`);
104 }
105 if (!this.useWatchFile && fs.watch) {
106 if (this.logger) {
107 this.logger.info("watch strategy: watch");
108 }
109 return this.watcher = fs.watch(this.filename, this.fsWatchOptions, (e, filename) => {
110 return this.watchEvent(e, filename);
111 });
112 } else {
113 if (this.logger) {
114 this.logger.info("watch strategy: watchFile");
115 }
116 return fs.watchFile(this.filename, this.fsWatchOptions, (curr, prev) => {
117 return this.watchFileEvent(curr, prev);
118 });
119 }
120 }
121
122 watchEvent(e, filename) {
123 var err, stats;
124 if (e === 'change') {
125 try {
126 stats = fs.statSync(this.filename);
127 } catch (error1) {
128 err = error1;
129 if (this.logger) {
130 this.logger.error(`'${e}' event for ${this.filename}. ${err}`);
131 }
132 this.emit("error", `'${e}' event for ${this.filename}. ${err}`);
133 return;
134 }
135 if (stats.size < this.pos) { //scenario where texts is not appended but it's actually a w+
136 this.pos = stats.size;
137 }
138 if (stats.size > this.pos) {
139 this.queue.push({
140 start: this.pos,
141 end: stats.size
142 });
143 this.pos = stats.size;
144 if (this.queue.length === 1) {
145 return this.internalDispatcher.emit("next");
146 }
147 }
148 } else if (e === 'rename') {
149 //MacOS sometimes throws a rename event for no reason.
150 //Different platforms might behave differently.
151 //see https://nodejs.org/api/fs.html#fs_fs_watch_filename_options_listener
152 //filename might not be present.
153 //https://nodejs.org/api/fs.html#fs_filename_argument
154 //Better solution would be check inode but it will require a timeout and
155 // a sync file read.
156 if (filename === void 0 || filename !== this.filename) {
157 this.unwatch();
158 if (this.follow) {
159 return setTimeout((() => {
160 return this.watch();
161 }), 1000);
162 } else {
163 if (this.logger) {
164 this.logger.error(`'rename' event for ${this.filename}. File not available.`);
165 }
166 return this.emit("error", `'rename' event for ${this.filename}. File not available.`);
167 }
168 } else {
169
170 }
171 }
172 }
173
174 // @logger.info("rename event but same filename")
175 watchFileEvent(curr, prev) {
176 if (curr.size > prev.size) {
177 this.pos = curr.size; // Update @pos so that a consumer can determine if entire file has been handled
178 this.queue.push({
179 start: prev.size,
180 end: curr.size
181 });
182 if (this.queue.length === 1) {
183 return this.internalDispatcher.emit("next");
184 }
185 }
186 }
187
188 unwatch() {
189 if (this.watcher) {
190 this.watcher.close();
191 } else {
192 fs.unwatchFile(this.filename);
193 }
194 this.isWatching = false;
195 this.queue = [];
196 if (this.logger) {
197 return this.logger.info("Unwatch ", this.filename);
198 }
199 }
200
201};
202
203exports.Tail = Tail;