All files / lib/winston tail-file.js

61.29% Statements 38/62
37.5% Branches 12/32
66.67% Functions 4/6
61.29% Lines 38/62

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125                  6x 6x 6x                             6x 1x 1x 1x 1x 1x 1x   1x       1x 1x           1x 1x                   1x 44x         44x 44x                   44x 39x                       39x     5x 5x 5x     5x   5x 5x   5x   48x 48x 48x         48x     5x 5x 5x         1x 1x          
/**
 * tail-file.js: TODO: add file header description.
 *
 * (C) 2010 Charlie Robbins
 * MIT LICENCE
 */
 
'use strict';
 
const fs = require('fs');
const { StringDecoder } = require('string_decoder');
const { Stream } = require('readable-stream');
 
/**
 * Simple no-op function.
 * @returns {undefined}
 */
function noop() {}
 
/**
 * TODO: add function description.
 * @param {Object} options - Options for tail.
 * @param {function} iter - Iterator function to execute on every line.
* `tail -f` a file. Options must include file.
 * @returns {mixed} - TODO: add return description.
 */
module.exports = (options, iter) => {
  const buffer = Buffer.alloc(64 * 1024);
  const decode = new StringDecoder('utf8');
  const stream = new Stream();
  let buff = '';
  let pos = 0;
  let row = 0;
 
  Iif (options.start === -1) {
    delete options.start;
  }
 
  stream.readable = true;
  stream.destroy = () => {
    stream.destroyed = true;
    stream.emit('end');
    stream.emit('close');
  };
 
  fs.open(options.file, 'a+', '0644', (err, fd) => {
    Iif (err) {
      if (!iter) {
        stream.emit('error', err);
      } else {
        iter(err);
      }
      stream.destroy();
      return;
    }
 
    (function read() {
      Iif (stream.destroyed) {
        fs.close(fd, noop);
        return;
      }
 
      return fs.read(fd, buffer, 0, buffer.length, pos, (err, bytes) => {
        Iif (err) {
          if (!iter) {
            stream.emit('error', err);
          } else {
            iter(err);
          }
          stream.destroy();
          return;
        }
 
        if (!bytes) {
          Iif (buff) {
            // eslint-disable-next-line eqeqeq
            if (options.start == null || row > options.start) {
              if (!iter) {
                stream.emit('line', buff);
              } else {
                iter(null, buff);
              }
            }
            row++;
            buff = '';
          }
          return setTimeout(read, 1000);
        }
 
        let data = decode.write(buffer.slice(0, bytes));
        Eif (!iter) {
          stream.emit('data', data);
        }
 
        data = (buff + data).split(/\n+/);
 
        const l = data.length - 1;
        let i = 0;
 
        for (; i < l; i++) {
          // eslint-disable-next-line eqeqeq
          Eif (options.start == null || row > options.start) {
            Eif (!iter) {
              stream.emit('line', data[i]);
            } else {
              iter(null, data[i]);
            }
          }
          row++;
        }
 
        buff = data[l];
        pos += bytes;
        return read();
      });
    }());
  });
 
  Eif (!iter) {
    return stream;
  }
 
  return stream.destroy;
};