重写console.error

1,379 阅读1分钟

版权声明:本文为博主原创文章,未经博主允许不得转载。 文章底部留言可联系作者。

一、背景

在开发过程中发现页面上 antd 的表单 onFinish 方法中的js错误不能正常上报,先开始以为是被react捕获了没有再抛出,所以window.onerror捕获不到,查看了一些相关的文章

import { Form, Divider, Input, Button } from "antd";

export default function App() {
  // onFinish事件错误可window.onerror不能捕获
  const onFinish = (values) => {
    a;
  };

  return (
    <div className="App">
      <Form
        ...
        onFinish={onFinish}
      >
       ...
        <Form.Item>
          <Button type="primary" htmlType="submit">
            提交
          </Button>
        </Form.Item>
      </Form>
    </div>
  );
}

image.png

react 错误边界#关于事件处理器

后来断点发现是antdonFinish方法内部try catch 捕获了错误然后 console.error 打印的。

image.png

为了可以顺利上报就想到了重新console.error 并在重写的方法里直接上报。

二、重写console.error

因为antd中是通过 try catch 捕获的error,获取error中的信息 主要包含:

try { 
// ... 
} catch(err) {// <-- “error 对象”,也可以用其他参数名代替 err 
// ... 
}
  • name

    Error 名称。例如,对于一个未定义的变量,名称是 "ReferenceError"

  • message

    关于 error 的详细文字描述。

  • stack

    当前的调用栈:用于调试目的的一个字符串,其中包含有关导致 error 的嵌套调用序列的信息。

try {
  lalala; // error, variable is not defined!
} catch(err) {
  alert(err.name); // ReferenceError
  alert(err.message); // lalala is not defined
  alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)

  // 也可以将一个 error 作为整体显示出来as a whole
  // Error 信息被转换为像 "name: message" 这样的字符串
  alert(err); // ReferenceError: lalala is not defined
}

我们了解到我们 console.error 中打印的是什么以后就可以针对的重写并上报这个错误了

 // 重写console.error
 const oldError = console.error;
 
 console.error = function (error) {
     if (error != '参数有缺失') {
         const message = error.message;
         const stack = error.stack;
         let row = 0, column = 0, url = null;
         if (stack) {
             let mres = stack.match(/\(.*?\)/g) || []
             let firstLine = (mres[0] || "").replace("(", "").replace(")", "") // 获取到堆栈信息的第一条

             // 根据:分隔获取行列
             let info = firstLine.split(':') 
             row = info[info.length - 2] // 行
             column = info[info.length - 1] // 列
              // 获取报错文件url
             url = [...info].slice(0, info.length - 2).join(':');
         }

         setTimeout(function () {
             // 上报错误内容
             let opt = {
                 url,
                 row,
                 column,
                 message,
                 stack // 错误堆栈信息
             }
             //进行上报的方法
             ...
         }, 0);
     }
     return oldError.apply(console, arguments);
 };

三、其他

  1. 除了上面提到的 antd onFinish 捕获了错误导致 window.onerror 无法捕获到之外,在普通的 onClick 事件中发现是不受影响的。
import { Button } from "antd";

...

  // 普通点击事件错误可以通过window.onerror捕获
const clickHandler = () => {
    b;
};

return (
      <Button onClick={clickHandler}>测试普通点击</Button>
)

image.png

  1. react组件渲染导致的报错,因为使用了错误边界(ErrorBoundary)我们可以看到react内部也会console.error打印的错误,但是并没有捕获
export default function App() {
  ...
  
  cccc //这里会渲染的时候报错

  return (
    <div className="App">
      ...
    </div>
  );

通过打印的堆栈信息点击进去我们看一看到打印报错的地方: image.png

以上demo代码请查看

参考文章