123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265 |
- /**
- * exception-handler.js: Object for handling uncaughtException events.
- *
- * (C) 2010 Charlie Robbins
- * MIT LICENCE
- */
- 'use strict';
- function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
- function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
- function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
- function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
- function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
- var os = require('os');
- var asyncForEach = require('async/forEach');
- var debug = require('@dabh/diagnostics')('winston:exception');
- var once = require('one-time');
- var stackTrace = require('stack-trace');
- var ExceptionStream = require('./exception-stream');
- /**
- * Object for handling uncaughtException events.
- * @type {ExceptionHandler}
- */
- module.exports = /*#__PURE__*/function () {
- /**
- * TODO: add contructor description
- * @param {!Logger} logger - TODO: add param description
- */
- function ExceptionHandler(logger) {
- _classCallCheck(this, ExceptionHandler);
- if (!logger) {
- throw new Error('Logger is required to handle exceptions');
- }
- this.logger = logger;
- this.handlers = new Map();
- }
- /**
- * Handles `uncaughtException` events for the current process by adding any
- * handlers passed in.
- * @returns {undefined}
- */
- return _createClass(ExceptionHandler, [{
- key: "handle",
- value: function handle() {
- var _this = this;
- for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
- args[_key] = arguments[_key];
- }
- args.forEach(function (arg) {
- if (Array.isArray(arg)) {
- return arg.forEach(function (handler) {
- return _this._addHandler(handler);
- });
- }
- _this._addHandler(arg);
- });
- if (!this.catcher) {
- this.catcher = this._uncaughtException.bind(this);
- process.on('uncaughtException', this.catcher);
- }
- }
- /**
- * Removes any handlers to `uncaughtException` events for the current
- * process. This does not modify the state of the `this.handlers` set.
- * @returns {undefined}
- */
- }, {
- key: "unhandle",
- value: function unhandle() {
- var _this2 = this;
- if (this.catcher) {
- process.removeListener('uncaughtException', this.catcher);
- this.catcher = false;
- Array.from(this.handlers.values()).forEach(function (wrapper) {
- return _this2.logger.unpipe(wrapper);
- });
- }
- }
- /**
- * TODO: add method description
- * @param {Error} err - Error to get information about.
- * @returns {mixed} - TODO: add return description.
- */
- }, {
- key: "getAllInfo",
- value: function getAllInfo(err) {
- var message = null;
- if (err) {
- message = typeof err === 'string' ? err : err.message;
- }
- return {
- error: err,
- // TODO (indexzero): how do we configure this?
- level: 'error',
- message: ["uncaughtException: ".concat(message || '(no error message)'), err && err.stack || ' No stack trace'].join('\n'),
- stack: err && err.stack,
- exception: true,
- date: new Date().toString(),
- process: this.getProcessInfo(),
- os: this.getOsInfo(),
- trace: this.getTrace(err)
- };
- }
- /**
- * Gets all relevant process information for the currently running process.
- * @returns {mixed} - TODO: add return description.
- */
- }, {
- key: "getProcessInfo",
- value: function getProcessInfo() {
- return {
- pid: process.pid,
- uid: process.getuid ? process.getuid() : null,
- gid: process.getgid ? process.getgid() : null,
- cwd: process.cwd(),
- execPath: process.execPath,
- version: process.version,
- argv: process.argv,
- memoryUsage: process.memoryUsage()
- };
- }
- /**
- * Gets all relevant OS information for the currently running process.
- * @returns {mixed} - TODO: add return description.
- */
- }, {
- key: "getOsInfo",
- value: function getOsInfo() {
- return {
- loadavg: os.loadavg(),
- uptime: os.uptime()
- };
- }
- /**
- * Gets a stack trace for the specified error.
- * @param {mixed} err - TODO: add param description.
- * @returns {mixed} - TODO: add return description.
- */
- }, {
- key: "getTrace",
- value: function getTrace(err) {
- var trace = err ? stackTrace.parse(err) : stackTrace.get();
- return trace.map(function (site) {
- return {
- column: site.getColumnNumber(),
- file: site.getFileName(),
- "function": site.getFunctionName(),
- line: site.getLineNumber(),
- method: site.getMethodName(),
- "native": site.isNative()
- };
- });
- }
- /**
- * Helper method to add a transport as an exception handler.
- * @param {Transport} handler - The transport to add as an exception handler.
- * @returns {void}
- */
- }, {
- key: "_addHandler",
- value: function _addHandler(handler) {
- if (!this.handlers.has(handler)) {
- handler.handleExceptions = true;
- var wrapper = new ExceptionStream(handler);
- this.handlers.set(handler, wrapper);
- this.logger.pipe(wrapper);
- }
- }
- /**
- * Logs all relevant information around the `err` and exits the current
- * process.
- * @param {Error} err - Error to handle
- * @returns {mixed} - TODO: add return description.
- * @private
- */
- }, {
- key: "_uncaughtException",
- value: function _uncaughtException(err) {
- var info = this.getAllInfo(err);
- var handlers = this._getExceptionHandlers();
- // Calculate if we should exit on this error
- var doExit = typeof this.logger.exitOnError === 'function' ? this.logger.exitOnError(err) : this.logger.exitOnError;
- var timeout;
- if (!handlers.length && doExit) {
- // eslint-disable-next-line no-console
- console.warn('winston: exitOnError cannot be true with no exception handlers.');
- // eslint-disable-next-line no-console
- console.warn('winston: not exiting process.');
- doExit = false;
- }
- function gracefulExit() {
- debug('doExit', doExit);
- debug('process._exiting', process._exiting);
- if (doExit && !process._exiting) {
- // Remark: Currently ignoring any exceptions from transports when
- // catching uncaught exceptions.
- if (timeout) {
- clearTimeout(timeout);
- }
- // eslint-disable-next-line no-process-exit
- process.exit(1);
- }
- }
- if (!handlers || handlers.length === 0) {
- return process.nextTick(gracefulExit);
- }
- // Log to all transports attempting to listen for when they are completed.
- asyncForEach(handlers, function (handler, next) {
- var done = once(next);
- var transport = handler.transport || handler;
- // Debug wrapping so that we can inspect what's going on under the covers.
- function onDone(event) {
- return function () {
- debug(event);
- done();
- };
- }
- transport._ending = true;
- transport.once('finish', onDone('finished'));
- transport.once('error', onDone('error'));
- }, function () {
- return doExit && gracefulExit();
- });
- this.logger.log(info);
- // If exitOnError is true, then only allow the logging of exceptions to
- // take up to `3000ms`.
- if (doExit) {
- timeout = setTimeout(gracefulExit, 3000);
- }
- }
- /**
- * Returns the list of transports and exceptionHandlers for this instance.
- * @returns {Array} - List of transports and exceptionHandlers for this
- * instance.
- * @private
- */
- }, {
- key: "_getExceptionHandlers",
- value: function _getExceptionHandlers() {
- // Remark (indexzero): since `logger.transports` returns all of the pipes
- // from the _readableState of the stream we actually get the join of the
- // explicit handlers and the implicit transports with
- // `handleExceptions: true`
- return this.logger.transports.filter(function (wrap) {
- var transport = wrap.transport || wrap;
- return transport.handleExceptions;
- });
- }
- }]);
- }();
|