'use strict'; const assert = require('assert'), path = require('path'), spawn = require('child_process').spawn, stream = require('stream'), { StringDecoder } = require('string_decoder'), TransformStream = stream.Transform; const LOGTAIL_CMD = path.join(__dirname, 'scripts/logtail.sh'); class LogStream extends TransformStream { constructor(options) { super(); this._options = Object.assign({ source: 'unknown', format: 'json' }, options); this._decoder = new StringDecoder(); this._soFar = ''; } _format(line) { if (this._options.format !== 'json') return line + '\n'; const data = line.split(' '); // logs are let timestamp = (new Date(data[0])).getTime(); if (isNaN(timestamp)) timestamp = 0; const message = line.slice(data[0].length+1); // ignore faulty empty logs if (!timestamp && !message) return; return JSON.stringify({ realtimeTimestamp: timestamp * 1000, message: message, source: this._options.source }) + '\n'; } _transform(chunk, encoding, callback) { const data = this._soFar + this._decoder.write(chunk); let start = this._soFar.length, end = -1; while ((end = data.indexOf('\n', start)) !== -1) { const line = data.slice(start, end); // does not include end this.push(this._format(line)); start = end + 1; } this._soFar = data.slice(start); callback(null); } _flush(callback) { const line = this._soFar + this._decoder.end(); this.push(this._format(line)); callback(null); } } function tail(filePaths, options) { assert(Array.isArray(filePaths)); assert.strictEqual(typeof options, 'object'); const lines = options.lines === -1 ? '+1' : options.lines, follow = options.follow; const args = [ '--lines=' + lines ]; if (follow) args.push('--follow'); return spawn(LOGTAIL_CMD, args.concat(filePaths)); } function journalctl(unit, options) { assert.strictEqual(typeof unit, 'string'); assert.strictEqual(typeof options, 'object'); const args = []; args.push('--lines=' + (options.lines === -1 ? 'all' : options.lines)); args.push(`--unit=${unit}`); args.push('--no-pager'); args.push('--output=short-iso'); if (options.follow) args.push('--follow'); return spawn('journalctl', args); } exports = module.exports = { tail, journalctl, LogStream };