gx
chenyc
2025-02-12 ea42ff3ebee1eeb3fb29423aa848a249441db81c
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
/**
 * 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:rejection');
var once = require('one-time');
var stackTrace = require('stack-trace');
var RejectionStream = require('./rejection-stream');
 
/**
 * Object for handling unhandledRejection events.
 * @type {RejectionHandler}
 */
module.exports = /*#__PURE__*/function () {
  /**
   * TODO: add contructor description
   * @param {!Logger} logger - TODO: add param description
   */
  function RejectionHandler(logger) {
    _classCallCheck(this, RejectionHandler);
    if (!logger) {
      throw new Error('Logger is required to handle rejections');
    }
    this.logger = logger;
    this.handlers = new Map();
  }
 
  /**
   * Handles `unhandledRejection` events for the current process by adding any
   * handlers passed in.
   * @returns {undefined}
   */
  return _createClass(RejectionHandler, [{
    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._unhandledRejection.bind(this);
        process.on('unhandledRejection', this.catcher);
      }
    }
 
    /**
     * Removes any handlers to `unhandledRejection` 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('unhandledRejection', 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: ["unhandledRejection: ".concat(message || '(no error message)'), err && err.stack || '  No stack trace'].join('\n'),
        stack: err && err.stack,
        rejection: 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.handleRejections = true;
        var wrapper = new RejectionStream(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: "_unhandledRejection",
    value: function _unhandledRejection(err) {
      var info = this.getAllInfo(err);
      var handlers = this._getRejectionHandlers();
      // 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 rejection 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 rejections from transports when
          // catching unhandled rejections.
          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: "_getRejectionHandlers",
    value: function _getRejectionHandlers() {
      // 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
      // `handleRejections: true`
      return this.logger.transports.filter(function (wrap) {
        var transport = wrap.transport || wrap;
        return transport.handleRejections;
      });
    }
  }]);
}();