1 |
|
2 | var Tail, environment, events, fs,
|
3 | boundMethodCheck = function(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new Error('Bound instance method accessed before binding'); } };
|
4 |
|
5 | events = require("events");
|
6 |
|
7 | fs = require('fs');
|
8 |
|
9 | environment = process.env['NODE_ENV'] || 'development';
|
10 |
|
11 | Tail = 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) {
|
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 |
|
150 |
|
151 |
|
152 |
|
153 |
|
154 |
|
155 |
|
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 |
|
175 | watchFileEvent(curr, prev) {
|
176 | if (curr.size > prev.size) {
|
177 | this.pos = curr.size;
|
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 |
|
203 | exports.Tail = Tail;
|