有没有一种好方法可以跨事件发射器/事件循环边界在生产中显示错误跟踪?

lxe*_*lxe 7 javascript error-handling node.js

假设我有:

const req = require('net').createConnection(80, 'localhost');

req.on('error', (error) => {
  console.error(error) // handle somehow
});
Run Code Online (Sandbox Code Playgroud)

如果发生错误事件,我得到

{ Error: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14)
  errno: 'ECONNREFUSED',
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 80 }
Run Code Online (Sandbox Code Playgroud)

在航站楼。

我想看看完整的堆栈:

{ Error: connect ECONNREFUSED 127.0.0.1:80
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1117:14)
    at Object.<anonymous> (/Users/aleksey/error-handing-example.js:2:7)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
    at startup (internal/bootstrap/node.js:283:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:743:3)
  errno: 'ECONNREFUSED',
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 80 }
Run Code Online (Sandbox Code Playgroud)

注意:我像这样人为地获得了完整的跟踪:

Error.captureStackTrace(this);
const req = require('net').createConnection(80, 'localhost');

req.on('error', (error) => {
  error.stack += this.stack.replace(/^.+$/, '');
  console.error(error) // handle somehow
});
Run Code Online (Sandbox Code Playgroud)

我不能对所有错误都这样做,因为它不可持续

有没有办法做到这一点:

  • A。以一种不损害生产稳定性的方式(longjohn 声称它仅用于开发)?
  • b. 自动地?

lxe*_*lxe 4

这是我使用async_hooks和重写的一个 hacky 东西Error.captureStackTrace

const ah = require('async_hooks');
const traces = new Map();

ah.createHook({
  init(id) {
    const _trace = {};
    Error.captureStackTrace(_trace);
    traces.set(id, _trace.stack.replace(/(^.+$\n){4}/m, '\n'));
  },
  destroy(id) {
    traces.delete(id);
  },
}).enable();

global.Error = class extends Error {
  constructor(message) {
    super(message);
    this.constructor.captureStackTrace(this, this.constructor);
  }

  static captureStackTrace(what, where) {
    super.captureStackTrace.call(Error, what, where);
    const trace = traces.get(ah.executionAsyncId());
    if (trace) what.stack += trace;
  }
};
Run Code Online (Sandbox Code Playgroud)

要点是:

  1. 保留 asyncId => 痕迹的映射
  2. 覆盖Error.captureStackTrace以使用来自原始堆栈顶部的 asyncId 的跟踪

不确定它的性能限制、错误和鲁棒性,但这就是我能想到的。

现在如果我有:

const req = net.createConnection(8080);
req.once('error', err => {
  console.error(err);
});
Run Code Online (Sandbox Code Playgroud)

代替

{ Error: connect ECONNREFUSED 127.0.0.1:8080
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1084:14)
Run Code Online (Sandbox Code Playgroud)

我明白了

{ Error: connect ECONNREFUSED 127.0.0.1:8080
    at TCPConnectWrap.afterConnect [as oncomplete] (net.js:1084:14)
    at TCPConnectWrap.emitInitNative (internal/async_hooks.js:137:43)
    at internalConnect (net.js:840:26)
    at defaultTriggerAsyncIdScope (internal/async_hooks.js:294:19)
    at GetAddrInfoReqWrap.emitLookup [as callback] (net.js:1006:9)
    at GetAddrInfoReqWrap.onlookup [as oncomplete] (dns.js:62:10)
    at GetAddrInfoReqWrap.emitInitNative (internal/async_hooks.js:137:43)
    at lookup (dns.js:142:19)
    at net.js:981:5
    at defaultTriggerAsyncIdScope (internal/async_hooks.js:294:19)
    at lookupAndConnect (net.js:980:3)
    at Socket.connect (net.js:915:5)
    at Object.connect (net.js:162:17)
    at Object.<anonymous> (/Users/aleksey/epudos-lite/server/test.js:28:17)
Run Code Online (Sandbox Code Playgroud)