如何 jQuery Ajax 错误捕获和报告

How to jQuery Ajax Error Catch and Report

提问人:mars328 提问时间:6/28/2019 最后编辑:mars328 更新时间:8/28/2019 访问量:1208

问:

以什么方式获取为 js 错误报告生成的 ajaxError 事件的函数调用方?

我已经用jQuery构建了一个js错误存储库应用程序,我可以处理全局发生的正常js错误,但是我在ajax错误方面遇到了问题。 当是正常错误时,我可以得到错误的行号! 我试图使用全局ajax处理程序之一“ajax error”捕获它们,但我不确定如何获取该ajax调用者或调用者名称的行号。

请看底部!

const error_log_url = '/log';
const errorPost = function (data) {
    $.ajax({
         url: error_log_url,
         type: 'post',
         data: data,
         success: function (res) {
             console.log(res)
         }, error: function (res) {
            console.log(res)
         }
     })
 }
 window.addEventListener('error', function (e) {

     let params = {
         message: e.message || "Exception Handler",
         url: e.filename || "",
         lineno: e.lineno || 0,
         colno: e.colno || 0
     }

     errorPost(params)
 }, true);

 // wrap function for new error stack with adding event to certain element
 window.wrap = function (func) {
    // make sure you only wrap the function once
    if (!func._wrapped) {
        func._wrapped = function () {
            try {
                func.apply(this, arguments);
            } catch (exception) {
                throw exception
            }
        }
    }
    return func._wrapped;
 }

 // override add & remove event listeners with above wrap function
 let addEvenListener = window.EventTarget.prototype.addEventListener;
 window.EventTarget.prototype.addEventListener = function (event, callback, bubble) {
    addEvenListener.call(this, event, wrap(callback), bubble);
 }

 let removeEventLister = window.EventTarget.prototype.removeEventListener;
 window.EventTarget.prototype.removeEventListener = function (event, callback, bubble) {
    removeEventLister.call(this, event, callback._wrapped || callback, bubble);
 }

 $(document).ajaxError(function( event, jqxhr, settings, thrownError ) {

     // please look at here, how can I get the caller name that produced this error!
     console.log(arguments.callee.caller)

     if (settings.url != error_log_url)
         errorPost({
             message: event.type,
             filename: event.currentTarget.location.origin + settings.url
         })


 });

console.log(arguments.callee.caller) 这将打印出 null。

你看,我可以从 ErrorEvent 中获取更多信息,但我无法从 ajaxError 事件中获取详细信息,例如行号!

JavaScript jQuery HTML AJAX 错误报告

评论

0赞 mars328 6/28/2019
@CertainPerformance,是的,对,你知道谷歌浏览器是如何知道哪一行导致错误的吗?我认为最好遵循浏览器的方式!
0赞 CertainPerformance 6/28/2019
感谢您的澄清。对于一个具体的例子,你是否想,例如,如果它的ajax在你的处理程序中抛出,你是否想得到名称(和那里的行号),是这样吗?errorPost$.ajaxajaxError
0赞 mars328 6/28/2019
@CertainPerformance,是的,没错,不仅仅是 errorPost,还有所有可能产生 ajax 错误的函数的名称或行数!你说得有道理!

答:

3赞 CertainPerformance 6/28/2019 #1

不幸的是,看起来没有网络错误的全局事件

不过,有一种笨拙的方法可以解决这个问题 - 如果你将一个函数附加到方法上,该方法在发送请求时运行,你可以立即抛出一个错误,然后抓住它并检查堆栈以找出调用者。然后,将适当的堆栈行放入一个可以稍后检查的堆栈行中,并由对象索引。之后,如果请求失败,在处理程序中,使用其对象在 WeakMap 中查找堆栈。例如:ajaxSendWeakMapjqXHRajaxErrorjqXHR

$(document).ajaxError(function(event, jqxhr, settings, thrownError) {
  console.log(stacksByXHR.get(jqxhr));
});
const stacksByXHR = new WeakMap();
$(document).ajaxSend((event, jqXHR) => {
  try {
    throw new Error();
  } catch({ stack }) {
    let callCountNonJquery = 0;
    const foundCall = stack
      .split('\n')
      .slice(1) // Remove the top "Error" line, contains no information
      .find(line => {
        if (line.includes('jquery')) {
          return false;
        }
        callCountNonJquery++;
        // First call would be the thrown error above
        // Second call is the $.ajax initiator
        if (callCountNonJquery === 2) {
          return true;
        }
      });
    stacksByXHR.set(jqXHR, foundCall);
  }
});
$.ajax('/DoesNotExist');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

在我的机器上,这向我展示了

at https://stacksnippets.net/js:40:3

对应于以下行:$.ajax('/DoesNotExist');

enter image description here

如果 在函数内部,则函数名称也将在堆栈中可见,例如:$.ajax

$(document).ajaxError(function(event, jqxhr, settings, thrownError) {
  console.log(stacksByXHR.get(jqxhr));
});
const stacksByXHR = new WeakMap();
$(document).ajaxSend((event, jqXHR) => {
  try {
    throw new Error();
  } catch({ stack }) {
    let callCountNonJquery = 0;
    const foundCall = stack
      .split('\n')
      .slice(1) // Remove the top "Error" line, contains no information
      .find(line => {
        if (line.includes('jquery')) {
          return false;
        }
        callCountNonJquery++;
        // First call would be the thrown error above
        // Second call is the $.ajax initiator
        if (callCountNonJquery === 2) {
          return true;
        }
      });
    stacksByXHR.set(jqXHR, foundCall);
  }
});
function myFunctionWhichRunsAjax() {
  $.ajax('/DoesNotExist');
}
myFunctionWhichRunsAjax();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

评论

0赞 mars328 6/28/2019
如果周期性地进行许多 ajax 调用,并且像 setTimeout 这样产生错误,那么 weakmap 可能会内存不足怎么办?
0赞 CertainPerformance 6/28/2019
不,因为它是 ,而不是 - 如果除了 之外没有别的东西对 有剩余的引用,它就会从 中删除,所以只要你的代码的其他部分不保留对它的引用,从长远来看,内存使用量就不应该堆积。WeakMapMapWeakMapjqXHRWeakMap
0赞 mars328 6/28/2019
完美,非常感谢,顺便说一句,对于我所说的“setTimeout”之类的问题,有没有更好的建议?如果我定期调用ajax,它会产生很多错误,并且它们来自同一个url,数据库充满了相同的错误。我想保存最多 5 个相同的错误。
1赞 CertainPerformance 6/28/2019
您可以创建另一个对象,以错误抛出位置为索引,其值是该位置抛出的次数(可能与错误名称或类似名称连接)。对于每个错误,将相应的属性值递增 1,如果该值大于 5,则忽略该错误(不要将其发送到error_log_url)