In what way to get the function caller of ajaxError event produced for js error reporting?
I have built a js error repo app with jQuery and I can handle normal js errors occurred globally, but I have problems on ajax errors. I can get the line number of error when it is a normal error! I tried to catch them with one of global ajax handlers "ajax error", but I am not sure how to get line number of that ajax caller or caller name.
please look at the bottom part!
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) this prints out null.
you see, I can get much more info from ErrorEvent, but I can not get detailed info like line number from ajaxError event!
Unfortunately, it looks like there is no global event for network errors.
There is a hacky way to figure it out, though - if you attach a function to the ajaxSend
method, which runs when a request is sent, you can throw an error immediately, then catch it and examine the stack to figure out the caller. Then, put the appropriate stack line into a WeakMap
which can be examined later, indexed by the jqXHR
object. Afterwards, if the request fails, in the ajaxError
handler, use its jqXHR
object to look up the stack in the WeakMap. For example:
$(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>
On my machine, this shows me
at https://stacksnippets.net/js:40:3
which corresponds to the $.ajax('/DoesNotExist');
line:
If the $.ajax
is inside a function, the function name will be visible in the stack too, for example:
$(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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With