如何在角度错误上获得更好的堆栈跟踪



当前,当我在生产Angular(v7)应用中遇到错误时,我会得到这样的堆栈跟踪。但这实际上不可能从中获取任何有意义的信息。如何获得更好的堆栈跟踪,以便可以缩小此难以捉摸的错误来自哪里?目前,我在产品中遇到了这个错误,因为它从未发生在我的开发机器上,而且我不知道如何隔离它,因为stacktrace对我来说实际上是没有用的。我习惯于像C#这样的语言,您会得到一个非常简洁的stacktrace,它会给您一个错误的函数。此堆栈跟踪没有意义。

TypeError: Cannot read property 'appendChild' of null
at create_dom_structure (eval at  (:15:1), :1:16231)
at load_map (eval at  (:15:1), :1:101028)
at Object.load (eval at  (:15:1), :1:107834)
at eval (eval at  (:15:1), :1:108251)
at t.invokeTask (https://mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8844)
at Object.onInvokeTask (https://mywebsite.com/main.25a9fda6ea42f4308b79.js:1:467756)
at t.invokeTask (https://mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:8765)
at e.runTask (https://mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:4026)
at e.invokeTask (https://mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:9927)
at invoke (https://mywebsite.com/polyfills.d8680adf69e7ebd1de57.js:1:9818)

我的错误处理程序:

export class AppErrorHandler extends ErrorHandler {
constructor(
    private _http: Http,
    private injector: Injector,
) {
    super();
}
public handleError(error: any): void {
    if (error.status === '401') {
        alert('You are not logged in, please log in and come back!');
    } else {
        const router = this.injector.get(Router);
        const reportOject = {
            status: error.status,
            name: error.name,
            message: error.message,
            httpErrorCode: error.httpErrorCode,
            stack: error.stack,
            url: location.href,
            route: router.url,
        };
        this._http.post(`${Endpoint.APIRoot}Errors/AngularError`, reportOject)
            .toPromise()
            .catch((respError: any) => {
                Utils.formatError(respError, `AppErrorHandler()`);
            });
    }
    super.handleError(error);
}
}

通常JavaScript堆栈痕迹更有用。不幸的是,在生产应用程序中,您通常会打开缩影,这就是为什么您会遇到如此不可读的混乱的原因。

如果您负担得起较大的JavaScript捆绑包,则关闭缩小可能是有用的,只是为了获得更好的堆栈跟踪。

如何执行此操作会有所不同,具体取决于您使用的Angular CLI版本。对于V7 CLI:

在文件angular.json中,设置以下属性

{
  ...
  "projects": {
    "my-project": {
      ...
      "architect": {
        "build": {
          ...
          "configurations": {
            "production": {
              "optimization": false,
              "buildOptimizer": false,
              ...
            }
    ...
}

此问题中的替代解决方案

下面的页面将进行堆栈跟踪,然后在错误位置之前和之后下载缩小的JS并选择100个字符。它将对所有级别进行此操作。

必须从同一域中访问并收集堆栈跟踪的相同部署。我敢肯定,相同的概念可以轻松地移植到节点,而没有任何跨域或路径限制。

它还具有一个可以用备用路径或辅助域名替换基本路径的按钮。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title></title>
  <script>
    var updateUrl = false;
    const scriptFiles = new Map()
    var newDivGroup = null;
    function clickDecode(updateUrlNew) { 
      updateUrl = updateUrlNew;
      decodeNow();
    }
    async function decodeNow() {
      var textOutput = document.getElementById('text-output');
      textOutput.innerHTML = '';
      var myString = document.getElementById('text-input').value;
      // console.log(myString);
      var myRegexp = /(https:[a-zA-Z0-9/.]+.js):([0-9]+):([0-9]+)/g;
      var matches = myString.matchAll(myRegexp);
      for (const match of matches) {
        newDivGroup = document.createElement("div");
        newDivGroup.style.marginBottom = '6px';
        newDivGroup.style.padding = '15px';
        newDivGroup.style.backgroundColor = '#eee';
        newDivGroup.style.border = '1px solid #555'
        textOutput.appendChild(newDivGroup);
        // logToOutput('-');
        logToOutput(match[0]);
        await showErrorLocation(match[1], match[2], match[3])
      }
    }
    function logToOutput(message) {
      const newDiv = document.createElement("div");
      const newContent = document.createTextNode(message);
      newDiv.appendChild(newContent);
      
      newDivGroup.appendChild(newDiv);
    }
    async function showErrorLocation(scriptUrl, row, position) {
      if(updateUrl) {
        scriptUrl = scriptUrl.toLowerCase();      
        scriptUrl = scriptUrl.replaceAll('/dist/', '/dist-old/');      
      }
      position = parseInt(position);
      if(!scriptFiles.has(scriptUrl)) {
        const fileContents = await downloadScriptFile(scriptUrl);
        scriptFiles.set(scriptUrl, fileContents);
      }
      const fileContents = scriptFiles.get(scriptUrl);
      const fileLines = fileContents.split('n');
      const fileLine = fileLines[row - 1];
      let before = fileLine.substring(position - 100, position - 1);
      let after = fileLine.substring(position - 1, position + 100);
      logToOutput(before);
      logToOutput(after);
    }
    function downloadScriptFile(filePath) {
      return new Promise((resolve, reject) => {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', filePath);
        xhr.responseType = 'text';
        xhr.send();
        xhr.onload = function() {
          if (xhr.status != 200) {
            console.error(`Error ${xhr.status}: ${xhr.statusText}`);
            reject();
          } else {
            // console.log(xhr.response);
            return resolve(xhr.response);
          }
        };
      });
    }
  </script>
<body style="padding: 30px;">
  <textarea id="text-input" rows="12" cols="150">
  </textarea>
  <br/>
  <button onclick="clickDecode(false)">Run (current deploy)</button>
  <button onclick="clickDecode(true)">Replace URL and Run (prior deploy)</button>
  <br/><br/>
  <div id="text-output">
  </div>
</body>
</html>

更新新角版本的信息

我们提供了一些Angular的调试辅助工具:https://developer.chrome.com/blog/devtools-better-angular-debugging/

但无论如何, Angular 15的新功能之一是更好的堆栈跟踪

随着最新版本的Angular,调试角度应用程序已简化,现在使用堆栈跟踪更加简单。Angular的开发团队努力达到追踪发展法规的标准,无论在整个角度开发生命周期内显示库。

开发此类堆栈跟踪的主要目的是改善错误消息的显示。例如,如果我们谈论先前的Angular版本,则开发人员会在代码发现阶段获得单线错误消息。还有很多处理冗长的程序来解决该错误。

首先,让我们查看摘要以获取以前的错误指示:

ERROR Error: Uncaught (in promise): Error
Error
   at app.component.ts:18:11
   at Generator.next (<anonymous>)
   at asyncGeneratorStep (asyncToGenerator.js:3:1)
   at _next (asyncToGenerator.js:25:1)
   at _ZoneDelegate.invoke (zone.js:372:26)
   at Object.onInvoke (core.mjs:26378:33)
   at _ZoneDelegate.invoke (zone.js:371:52)
   at Zone.run (zone.js:134:43)
   at zone.js:1275:36
   at _ZoneDelegate.invokeTask (zone.js:406:31)
   at resolvePromise (zone.js:1211:31)
   at zone.js:1118:17
   at zone.js:1134:33

开发人员由于以下原因无法理解错误段:

Third-party dependencies were solely responsible for such error message inputs.
You were not getting any information related to where such user interaction encountered this bug.
With an active and long collaboration with the Angular and Chrome DevTool team, it was pretty useful for the Angular community to perform integration with third-party dependencies (with the help of node_modules, zone.js, etc.); and thus, could achieve linked stack traces.

现在,使用Angular 15,您可以看到堆栈跟踪的改进如下所述:

ERROR Error: Uncaught (in promise): Error
Error
   at app.component.ts:18:11
   at fetch (async) 
   at (anonymous) (app.component.ts:4)
   at request (app.component.ts:4)
   at (anonymous) (app.component.ts:17)
   at submit (app.component.ts:15)
   at AppComponent_click_3_listener (app.component.html:4)

上面的代码显示了从遇到何处的错误消息信息,因此开发人员可以直接转到该代码部分并立即修复。

来源:

https://devtechnosys.com/insights/latest-angular-v15-features/

https://www.albiorixtech.com/blog/angular-15-best-features-new-updates/

最新更新