I have configured PHP to send me mails whenever there is an error. I would like to do the same with Javascript.
Also given the fact that this will be client side it is open to abuse.
What are good ways to get notified by mail when JS breaks in a web application?
Update:
Just to give some perspective, i usually load several js files including libraries (most of the time jQuery).
You can listen to the global onError event.
Note that you need to make sure it doesn't loop infinitely when it raises an error.
<script type="text/javascript">
var handlingError = false;
window.onerror = function() {
if(handlingError) return;
handlingError = true;
// process error
handlingError = false;
};
</script>
The code below relies on the global onError event, it does not require any external library and will work in any browser.
You should load it before any other script and make sure you have a server-side jserrorlogger.php script that picks up the error.
The code includes a very simple self-limiting mechanism: it will stop sending errors to the server after the 10th error. This comes in handy if your code gets stuck in a loop generating zillions of errors.
To avoid abuse you should include a similar self-limiting mechanism in your PHP code, for example by:
saving and updating a session variable with the error count and stop sending emails after X errors per session (while still writing them all down in your logs)
saving and updating a global variable with the errors-per-minute and stop sending emails when the threshold is exceeded
allowing only requests coming from authenticated users (applies only if your
application requires authentication)
you name it :)
Note that to better trace javascript errors you should wrap your relevant code in try/catch blocks and possibly use the printstacktrace function found here:
https://github.com/eriwen/javascript-stacktrace
<script type="text/javascript">
var globalOnError = (function() {
var logErrorCount = 0;
return function(err, url, line) {
logErrorCount++;
if (logErrorCount < 10) {
var msg = "";
if (typeof(err) === "object") {
if (err.message) {
// Extract data from webkit ErrorEvent object
url = err.filename;
line = err.lineno;
err = err.message;
} else {
// Handle strange cases where err is an object but not an ErrorEvent
buf = "";
for (var name in err) {
if (err.hasOwnProperty(name)) {
buf += name + "=" + err[name] + "&";
}
}
err = "(url encoded object): " + buf;
}
}
msg = "Unhandled exception ["+err+"] at line ["+line+"] url ["+url+"]";
var sc = document.createElement('script'); sc.type = 'text/javascript';
sc.src = 'jserrorlogger.php?msg='+encodeURIComponent(msg.substring(0, Math.min(800, msg.length)));
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(sc, s);
}
return false;
}
})();
window.onerror = globalOnError;
</script>
You would wrap your entire program in a try/catch and send caught exceptions over AJAX to the server where an email could be generated. Short of that (and I wouldn't do that) the answer is "not really."
JA Auide has the basic idea. You could also go somewhat in between, ie.:
Write an AJAX "errorNotify" function that sends error details to the server so that they can be emailed to you.
Wrap certain parts of your code (the chunks you expect might someday have issues) with a try/catch which invokes errorNotify in the catch block.
If you were truly concerned about having 0 errors whatsoever, you'd then be stuff with try/catching your whole app, but I think just try/catching the key blocks will give you 80% of the value for 20% of the effort.
Just a note from a person that logs JavaScript errors.
The info that comes from window.onerror is very generic. Makes debugging hard and you have no idea what caused it.
User's plugins can also cause the issue. A very common one in certain Firebug versions was toString().
You want to make sure that you do not flood your server with calls, limit the amount of errors that can be sent page per page load.
Make sure to log page url with the error call, grab any other information you can too to make your life easier to debug.
Related
I am debugging a javascript/html5 web app that uses a lot of memory. Occasionally I get an error message in the console window saying
"uncaught exception: out of memory".
Is there a way for me to gracefully handle this error inside the app?
Ultimately I need to re-write parts of this to prevent this from happening in the first place.
You should calclulate size of your localStorage,
window.localStorage is full
as a solution is to try to add something
var localStorageSpace = function(){
var allStrings = '';
for(var key in window.localStorage){
if(window.localStorage.hasOwnProperty(key)){
allStrings += window.localStorage[key];
}
}
return allStrings ? 3 + ((allStrings.length*16)/(8*1024)) + ' KB' : 'Empty (0 KB)';
};
var storageIsFull = function () {
var size = localStorageSpace(); // old size
// try to add data
var er;
try {
window.localStorage.setItem("test-size", "1");
} catch(er) {}
// check if data added
var isFull = (size === localStorageSpace());
window.localStorage.removeItem("test-size");
return isFull;
}
I also got the same error message recently when working on a project having lots of JS and sending Json, but the solution which I found was to update input type="submit" attribute to input type="button". I know there are limitations of using input type="button"..> and the solution looks weird, but if your application has ajax with JS,Json data, you can give it a try. Thanks.
Faced the same problem in Firefox then later I came to know I was trying to reload a HTML page even before setting up some data into local-storage inside if loop. So you need to take care of that one and also check somewhere ID is repeating or not.
But same thing was working great in Chrome. Maybe Chrome is more Intelligent.
Today I find the need to track and retrieve a Javascript error stacktrace to solve them.
Today we were able to capture all rest calls, the idea is that once you get an error, automatically posts the stacktrace of that error plus the responses of the rest saved services so we can detect, reproduce, and solve the problems in almost an identical environment/situation.
As a requirement we were asked to make a module that can be included without being intrusive, for example:
Include the module that contains the hook logic in one JS, would be not invasive, include several lines of code in various JS files would be invasive.
The goal is to make a tool that can be included in a system already developed and track error events (like console).
I've read about this trackers logic:
errorception.com/
trackjs.com/
atatus.com/
airbrake.io/
jslogger.com/
getsentry.com/
muscula.com/
debuggify.net/
raygun.io/home
We need to do something like that, track the error and send it to our server.
As "Dagg Nabbit" says... "It's difficult to get a stack trace from errors that happen "in the wild" right now"...
So, we got a lot of paid products, but how did they really works?
In Airbrake they use stacktrace and window.onerror:
window.onerror = function(message, file, line) {
setTimeout(function() {
Hoptoad.notify({
message : message,
stack : '()#' + file + ':' + line
});
}, 100);
return true;
};
But i cant figure out when the stacktrace really used.
At some point, stacktrace, raven.js and other trackers need try / catch.
what happens if we found a way to make a global wrapper?
Can we just call stacktrace and wait for the catch?
How can I send a stack trace to my server when an unexpected error occurs on the client? Any advice or good practices?
It's difficult to get a stack trace from errors that happen "in the wild" right now, because the Error object isn't available to window.onerror.
window.onerror = function(message, file, line) { }
There is also a new error event, but this event doesn't expose the Error object (yet).
window.addEventListener('error', function(errorEvent) { })
Soon, window.onerror will get a fifth parameter containing the Error object, and you can probably use stacktrace.js to grab a stack trace during window.onerror.
<script src="stacktrace.js"></script>
<script>
window.onerror = function(message, file, line, column, error) {
try {
var trace = printStackTrace({e: error}).join('\n');
var url = 'http://yourserver.com/?jserror=' + encodeURIComponent(trace);
var p = new printStackTrace.implementation();
var xhr = p.createXMLHTTPObject();
xhr.open('GET', url, true);
xhr.send(null);
} catch (e) { }
}
</script>
At some point the Error API will probably be standardized, but for now, each implementation is different, so it's probably smart to use something like stacktracejs to grab the stack trace, since doing so requires a separate code path for each browser.
I'm the cofounder of TrackJS, mentioned above. You are correct, sometimes getting the stack traces requires a little bit of work. At some level, async functions have to be wrapped in a try/catch block--but we do this automatically!
In TrackJS 2.0+, any function you pass into a callback (addEventListener, setTimeout, etc) will be automatically wrapped in a try/catch. We've found that we can catch nearly everything with this.
For the few things that we might now, you can always try/catch it yourself. We provide some helpful wrappers to help, for example:
function foo() {
// does stuff that might blow up
}
trackJs.watch(foo);
In latest browsers, there is a 5th parameter for error object in window.onerror.
In addEventListener, you can get error object by event.error
// Only Chrome & Opera pass the error object.
window.onerror = function (message, file, line, col, error) {
console.log(message, "from", error.stack);
// You can send data to your server
// sendData(data);
};
// Only Chrome & Opera have an error attribute on the event.
window.addEventListener("error", function (event) {
console.log(e.error.message, "from", event.error.stack);
// You can send data to your server
// sendData(data);
})
You can send data using image tag as follows
function sendData(data) {
var img = newImage(),
src = http://yourserver.com/jserror + '&data=' + encodeURIComponent(JSON.stringify(data));
img.crossOrigin = 'anonymous';
img.onload = function success() {
console.log('success', data);
};
img.onerror = img.onabort = function failure() {
console.error('failure', data);
};
img.src = src;
}
If you are looking for opensource, then you can checkout TraceKit. TraceKit squeezes out as much useful information as possible and normalizes it. You can register a subscriber for error reports:
TraceKit.report.subscribe(function yourLogger(errorReport) {
// sendData(data);
});
However you have to do backend to collect the data and front-end to visualize the data.
Disclaimer: I am a web developer at https://www.atatus.com/ where you can track all your JavaScript errors and filter errors across various dimensions such as browsers, users, urls, tags etc.
#Da3 You asked about appenlight and stacktraces. Yes it can gather full stacktraces as long as you wrap the exception in try/catch block. Otherwise it will try reading the info from window.onerror which is very limited. This is a browser limitation (which may be fixed in future).
I am getting a javascript error "Invalid argument on Line: 2 Char: 141544 in sp.ui.rte.js" on a SharePoint development. This appears to be a known issue from google within the SharePoint js files - http://wss.boman.biz/Lists/Posts/Post.aspx?List=c0143750-7a4e-4df3-9dba-8a3651407969&ID=69
After analysing the impact I decided that rather than changing the js in the SharePoint 14 hive I want to suppress the error message for just this error. I was trying to do the following:
ClientScriptManager cs = Page.ClientScript;
//Check to see if the startup script is already registered.
if (!cs.IsStartupScriptRegistered("Alert"))
{
StringBuilder cstext1 = new StringBuilder();
cstext1.Append("<script type=text/javascript> window.onerror = function (msg, url, num) {return true;} </");
cstext1.Append("script>");
cs.RegisterStartupScript(this.GetType(), "Alert", cstext1.ToString());
}
The problem is this will suppress all js errors on the page not just the sp.ui.rte.js ones. Is it possible to do a string search on the URL (http://SHAREPOINT/_layouts/sp.ui.rte.js?rev=uY%2BcHuH6ine5hasQwHX1cw%3D%3D - where the only consistent value between sites will be /_layouts/sp.ui.rte.js? ) to just search and suppress this exact error?
Thanks for any help!
Use try/catch block around code in JS. If message matches something you want to ignore, just do nothing. Propagate everything else.
Your original approach with .onerror would work too if you change it to analyze message and propagate everything not matching ignored string as well.
So far this has been the most successful fix I have found and currently using:
function fixRTEBug() {
// This Fix for parentElement bug in RTE should survive Service Packs and CU's
function SubstituteRTERangeParentElement() {
var originalRTERangeParentElement = RTE.Range.prototype.parentElement;
RTE.Range.prototype.parentElement = function () {
try {
originalRTERangeParentElement();
} catch (e) { }
}
}
SubstituteRTERangeParentElement();
}
ExecuteOrDelayUntilScriptLoaded(fixRTEBug, "sp.ui.rte.js");
I'm building a js library that reads binary files, including zip files.
Because there's no direct native support for arrays of binary data, when the zip files get large, there's a lot of copying that has to go on (See my other question for details).
This results in a "Stop Running this script?" alert. Now, I know this can happen if there's an infinite loop, but in my case, it's not an infinite loop. It's not a bug in the program. It just takes a loooooong time.
How can I suppress this?
This message is for security reason enabled, because otherwise you could simply block the users browser with a simple never ending loop. I think there no way to deactivate it.
Think about splitting you processing into serval parts, and schedule them via setTimeout, this should surpress the message, because the script is now not running all the time.
You could divide the process into increments, then use setTimeout to add a small delay.
In IE (and maybe Firefox too), the message is based on the number of statements executed, not the running time. If you can split some of the processing into a separate function, and defer it with setTimeout, I believe that it won't count toward the limit.
...answering my own question, so I could post the code I used.
The main issue was, I was reading the entire contents of a file, with a readToEnd() method, which actually reads one byte at a time. When reading a large file, it took a looooong time. The solution was to read asynchronously, and in batches.
This is the code I used:
readToEndAsync : function(callback) {
_state = "";
var slarge = "";
var s = "";
var txtrdr = this;
var readBatchAsync = function() {
var c = 0;
var ch = txtrdr.readChar();
while(ch != null)
{
s += ch;c++;
if(c > 1024)
{
slarge += s;
s = "";
break;
}
ch = txtrdr.readChar();
}
if (ch!=null){
setTimeout(readBatchAsync, 2);
}
else {
callback(slarge+s);
}
};
// kickoff
readBatchAsync();
return null;
},
And to call it:
textReader.readToEndAsync(function(out){
say("The callback is complete");
// the content is in "out"
});
I believe this feature is specific to Firefox and/or other browsers, and it has nothing to do with the javascript language itself.
As far as I know you (the programmer) have no way of stopping it in your visitors' browser.
I do a bunch of json requests with dynamic script tags. Is it possible to detect if there's an error in the request (eg. 503 error, 404 error) and run something on detection of the error?
use ajax instead. AFAIK there is no way to detect if a script tag loads or not, and if not, why it didn't load. Using ajax you can load the json and it will tell you why it didn't load.
Using a library like jQuery this becomes very simple:
$.ajax({
type: "GET",
url: "test.js",
dataType: "script",
error: function(xhr, error, exception){
alert(xhr.status); //Will alert 404 if the script does not exist
}
});
AFAIK, there's no way to access status code of some external asset loaded from the document (such as script, style or image). Even detecting error (via, say, onerror event handler) is not that widely supported across browsers.
If whatever you're loading falls under SOP, use XHR which gives you access to response headers. Otherwise, you can try looking into recently introduced X-domain XHR.
I'm assuming you want this to work cross-domain, which is why you can't use XHR?
Try creating two script tags for each request, the first does your standard JSONP request, the second is basically an error handler.
If the first script tag executes, then clear the error handler in your callback. But if the first gets a 404, the error handler inside the second script tag will be run.
You probably also want to set a timeout, to cope with a slow JSONP response.
http://www.phpied.com/javascript-include-ready-onload/ ?
If you're using jQuery, check out jQuery-JSONP which is a jQuery plugin that does a fairly decent job of doing the <script> insertion for you as well as detecting fetch errors.
Quoting from the project page, jQuery-JSONP features:
error recovery in case of network failure or ill-formed JSON responses,
precise control over callback naming and how it is transmitted in the URL,
multiple requests with the same callback name running concurrently,
two caching mechanisms (browser-based and page based),
the possibility to manually abort the request just like any other AJAX request,
a timeout mechanism.
If you need to cross domains (and need the page to work portably), you have to use dynamic script tags.
If you have access to the remote server, you can pass back an error code from the server, and have the server page return 200.
Whether you have access or not, you can use setTimeout when you create the script tag, passing a function that will trigger an error if it expires before the jsonp handler is called. Make sure that the jsonp handler aborts if the error handler has been called.
You'll need to track each request through a global collection, but you'll gain the ability to cancel and count requests. This is similar to the way that XHR objects are managed by a library like jQuery.
If you want to detect errors, listen for an error event and compare the fileName property of the error with the file name of the script. If they match, you then handle the error. The thing is, I think that the fileName property is Firefox and Opera-only. Most browsers that have a stacktrace for errors can also simulate this behaviour.
Here's an example, as requested by Eric Bréchemier:
var getErrorScriptNode = (function () {
var getErrorSource = function (error) {
var loc, replacer = function (stack, matchedLoc) {
loc = matchedLoc;
};
if ("fileName" in error) {
loc = error.fileName;
} else if ("stacktrace" in error) { // Opera
error.stacktrace.replace(/Line \d+ of .+ script (.*)/gm, replacer);
} else if ("stack" in error) { // WebKit
error.stack.replace(/at (.*)/gm, replacer);
loc = loc.replace(/:\d+:\d+$/, "");
}
return loc;
},
anchor = document.createElement("a");
return function (error) {
anchor.href = getErrorSource(error);
var src = anchor.href,
scripts = document.getElementsByTagName("script");
anchor.removeAttribute("href");
for (var i = 0, l = scripts.length; i < l; i++) {
anchor.href = scripts.item(i).src;
if (anchor.href === src) {
anchor.removeAttribute("href");
return scripts.item(i);
}
}
};
}());