UNPKG

winston-papertrail-transport

Version:
295 lines (294 loc) 12 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PapertrailConnection = exports.PapertrailTransport = void 0; var tls_1 = __importDefault(require("tls")); var os_1 = __importDefault(require("os")); var winston_1 = __importDefault(require("winston")); var triple_beam_1 = require("triple-beam"); var glossy_1 = require("glossy"); var winston_transport_1 = __importDefault(require("winston-transport")); var events_1 = require("events"); var net = __importStar(require("net")); var KEEPALIVE_INTERVAL = 15 * 1000; if (Number(winston_1.default.version.split('.')[0]) < 3) { throw new Error('winston-papertrail-transport requires winston >= 3.0.0'); } var PapertrailTransport = /** @class */ (function (_super) { __extends(PapertrailTransport, _super); function PapertrailTransport(options) { var _this = _super.call(this, options) || this; var defaultOptions = { host: 'localhost', port: 417, program: 'default', facility: 'daemon', hostname: os_1.default.hostname(), levels: { debug: 7, info: 6, notice: 5, warning: 4, warn: 4, error: 3, err: 3, crit: 2, alert: 1, emerg: 0, }, attemptsBeforeDecay: 5, maximumAttempts: 25, connectionDelay: 1000, handleExceptions: false, maxDelayBetweenReconnection: 60000, flushOnClose: true, disableTls: false, }; _this.options = Object.assign({}, defaultOptions, options); _this.connection = new PapertrailConnection(_this.options); _this.producer = new glossy_1.Produce({ facility: _this.options.facility, }); return _this; } Object.defineProperty(PapertrailTransport.prototype, "name", { get: function () { return 'papertrail'; }, enumerable: false, configurable: true }); PapertrailTransport.prototype.log = function (info, callback) { var _this = this; setImmediate(function () { _this.emit('logged', info); }); var _a = info, _b = triple_beam_1.LEVEL, level = _a[_b], _c = triple_beam_1.MESSAGE, output = _a[_c]; this.sendMessage(level, output, callback); }; PapertrailTransport.prototype.sendMessage = function (level, message, callback) { var lines = ['']; var msg = ''; // Only split if we actually have a message if (message) { lines = message.split('\n'); } // If the incoming message has multiple lines, break them and format each // line as its own message for (var i = 0; i < lines.length; i++) { // don't send extra message if our message ends with a newline if (lines[i].length === 0 && i === lines.length - 1) { break; } // Needs a valid severity - default to "notice" if the map failed. var severity = this.options.levels[level] || 5; msg += this.producer.produce({ severity: severity, host: this.options.hostname, appName: this.options.program, date: new Date(), message: lines[i], }) + '\r\n'; } this.connection.write(msg, callback); }; PapertrailTransport.prototype.close = function () { this.connection.close(); }; return PapertrailTransport; }(winston_transport_1.default)); exports.PapertrailTransport = PapertrailTransport; var PapertrailConnection = /** @class */ (function (_super) { __extends(PapertrailConnection, _super); function PapertrailConnection(options) { var _this = _super.call(this) || this; _this.options = options; if (!_this.options.host || !_this.options.port) { throw new Error('Missing required parameters: host and port'); } _this.connectionDelay = _this.options.connectionDelay; _this.currentRetries = 0; _this.totalRetries = 0; _this.loggingEnabled = true; _this._shutdown = false; _this._erroring = false; /** * Dev could instantiate a new logger and then call logger.log immediately. * We need a way to put incoming strings (from multiple transports) into * a buffer queue. */ _this.deferredQueue = []; _this.connect(); return _this; } PapertrailConnection.prototype.connect = function () { var _this = this; if (this._shutdown || this._erroring) { return; } this.close(); try { if (this.options.disableTls) { this.stream = net.createConnection(this.options.port, this.options.host, function () { return _this.onConnected(); }); this.stream.setKeepAlive(true, KEEPALIVE_INTERVAL); this.stream.once('error', function (err) { return _this.onErrored(err); }); this.stream.once('end', function () { return _this.connect(); }); } else { this.socket = net.createConnection(this.options.port, this.options.host, function () { _this.socket.setKeepAlive(true, KEEPALIVE_INTERVAL); _this.stream = tls_1.default.connect({ socket: _this.socket, rejectUnauthorized: false, }, function () { return _this.onConnected(); }); _this.stream.once('error', function (err) { _this.onErrored(err); }); _this.stream.once('end', function () { return _this.connect(); }); }); this.socket.once('error', function (err) { return _this.onErrored(err); }); } } catch (err) { this.onErrored(err); } }; PapertrailConnection.prototype.write = function (text, callback) { var _a; // If we cannot write at the moment, we add it to the deferred queue for later processing if ((_a = this.stream) === null || _a === void 0 ? void 0 : _a.writable) { try { this.stream.write(text, callback); } catch (e) { this.deferredQueue.push({ buffer: text, callback: callback }); } } else { this.deferredQueue.push({ buffer: text, callback: callback }); } }; PapertrailConnection.prototype.processBuffer = function () { if (this.deferredQueue.length === 0 || !this.stream || !this.stream.writable) { return; } var msg = this.deferredQueue.pop(); while (msg) { this.stream.write(msg.buffer, msg.callback); msg = this.deferredQueue.pop(); } this.stream.emit('empty'); }; PapertrailConnection.prototype.onConnected = function () { this.loggingEnabled = true; this.currentRetries = 0; this.totalRetries = 0; this.connectionDelay = this.options.connectionDelay; this.processBuffer(); this.emit('connect', "Connected to Papertrail at " + this.options.host + ":" + this.options.port); }; PapertrailConnection.prototype.onErrored = function (err) { var _this = this; this._erroring = true; this.emitSilentError(err); setTimeout(function () { if (_this.connectionDelay < _this.options.maxDelayBetweenReconnection && _this.currentRetries >= _this.options.attemptsBeforeDecay) { _this.connectionDelay = _this.connectionDelay * 2; _this.currentRetries++; } if (_this.loggingEnabled && _this.totalRetries >= _this.options.maximumAttempts) { _this.loggingEnabled = false; _this.emitSilentError(new Error('Maximum number of retries exceeded, disabling buffering')); } _this._erroring = false; _this.connect(); }, this.connectionDelay); }; PapertrailConnection.prototype.emitSilentError = function (err) { if (this.listenerCount('error') > 0) { this.emit('error', err); } else { console.error("Papertrail connection error: " + err); } }; PapertrailConnection.prototype.closeSockets = function () { if (this.socket) { this.socket.destroy(); this.socket = undefined; } if (this.stream) { this.stream.removeAllListeners('end'); this.stream.removeAllListeners('error'); this.stream.destroy(); this.stream = undefined; } }; PapertrailConnection.prototype.close = function () { var _this = this; // if we encounter errors while closing, we wait and try to close three more times var max = 3; var attempts = 0; this._shutdown = true; var _close = function () { try { if (_this.stream) { if (_this.options.flushOnClose && _this.deferredQueue.length > 0) { _this.stream.on('empty', function () { _this.closeSockets(); }); } else { _this.closeSockets(); } } _this._shutdown = false; } catch (e) { attempts++; if (attempts < max) { setTimeout(_close, 100 + attempts); } } }; _close(); }; return PapertrailConnection; }(events_1.EventEmitter)); exports.PapertrailConnection = PapertrailConnection;