如何 jQuery Ajax 错误捕获和报告

Posted

技术标签:

【中文标题】如何 jQuery Ajax 错误捕获和报告【英文标题】:How to jQuery Ajax Error Catch and Report 【发布时间】:2019-11-09 21:52:35 【问题描述】:

js报错产生的ajaxError事件的函数调用者如何获取?

我已经用 jQuery 构建了一个 js 错误 repo 应用程序,我可以处理全局发生的正常 js 错误,但是我在 ajax 错误上遇到了问题。 当是正常错误时,我可以得到错误的行号! 我试图用全局 ajax 处理程序之一“ajax 错误”来捕获它们,但我不确定如何获取该 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 事件中获得行号等详细信息!

【问题讨论】:

@CertainPerformance,是的,对,你知道谷歌浏览器是如何知道哪一行导致错误的吗?我认为按照浏览器的方式做比较好! 感谢您的澄清。举一个具体的例子,如果它的 ajax 在你的 ajaxError 处理程序中抛出,你想获取名称 errorPost(以及那里的 $.ajax 的行号),是这样吗? @CertainPerformance,是的,确切地说,不仅仅是 errorPost,而是所有可能产生 ajax 错误的函数的名称或行号!你说得对! 【参考方案1】:

很遗憾,网络错误似乎有no global event。

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

$(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');这一行:

如果$.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>

【讨论】:

如果有很多周期性的 ajax 调用,比如 setTimeout 产生错误,weakmap 可能内存不足? 不,因为它是 WeakMap,而不是 Map - 如果除了 WeakMap 之外没有其他对 jqXHR 的引用,它将从 WeakMap 中删除,因此只要代码的其他部分不保留对它的引用,从长远来看,内存使用不应堆积。 完美,非常感谢,顺便说一句,对于我所说的“setTimeout”之类的问题,有没有更好的建议?如果我定期调用 ajax 会产生很多错误,并且它们来自相同的 url,数据库中充满了相同的错误。我想保存最多 5 个相同的错误。 您可以创建另一个由错误抛出位置索引的对象,其值是该位置抛出的次数(可能与错误名称连接,或类似的东西)。在每个错误中,将相应的属性值加 1,如果该值大于 5,则忽略该错误(不要将其发送到 error_log_url

以上是关于如何 jQuery Ajax 错误捕获和报告的主要内容,如果未能解决你的问题,请参考以下文章

使用 JSONP 时如何捕获 jQuery $.getJSON(或数据类型设置为“jsonp”的 $.ajax)错误?

如何捕获 jQuery AJAX 错误?

如何在 ajax 调用成功中访问 jquery 对象 - 未捕获的错误:SYNTAX_ERR:DOM Exception 12

如何使用多个 Django FBV 通过 Ajax+jQuery 捕获和保存数据

如何使 JQuery-AJAX 请求同步

HTTP 错误 405.0 - 不允许使用 Jquery ajax get 的方法