Know your errors

Know your errors

JS errors, they come from browsers. In various forms, languages and if they are happening in a customer you don't see them. We have a jungle of browsers out there, between desktop, mobile, tablets, tv's etc. Different flavors with the same pain in the end. Log your JS errors, adding stack trace information for them using stacktrace.js. And a small dashboard to look into it. Presented @ #fronttrends 2013

171f449c85dfd0506812399782b14a26?s=128

Diogo Antunes

April 24, 2013
Tweet

Transcript

  1. KNOW YOUR ERRORS BECAUSE IT'S A JUNGLE OUT THERE

  2. WHO AM I? Diogo Antunes Booking.com Senior Client Side Developer

    @dicode
  3. OVERVIEW logging JS errors proof of concept tools/services

  4. SO WHY LOG JS ERRORS IF?

  5. YOU DO TDD

  6. YOU DO SELENIUM TESTS

  7. YOU DO HEADLESS BROWSER TESTING

  8. YOU HAVE A TESTING TEAM

  9. IT WORKS ON MY MACHINE

  10. IT'S A JUNGLE OUT THERE!

  11. 3RD PARTY SCRIPTS THAT YOU DON'T CONTROL Twitter, Facebook, GA

  12. YOU USE A CDN even if it is just to

    load jquery
  13. USERS INSTALL PLUGINS

  14. YOUR MARKETING DEPARTMENT LIKES TRACKING PIXELS

  15. YES, SOME THINGS YOU CANNOT CONTROL but some of them

    you can mitigate
  16. SOME CHALLENGES

  17. LOCALIZED MESSAGES Expected identifier, string or number Identificador esperado Se

    esperaba un identificador, una cadena o un número Bezeichner erwartet 标识 فّ د و ا
  18. LINE NUMBER minify js using gzip and preserving new lines

    may be a temporary solution
  19. CRYPTIC MESSAGES cannot find method of undefined Script error. cross

    origin domain policy Chrome and Firefox Access-Control-Allow-Origin: *
  20. BE SAFE deploy environment vars that disable 3rd party code

    if possible, without making any deploy to live that way you can take action quickly
  21. LET'S DO SOME LOGGING

  22. APPROACHES

  23. WINDOW.ONERROR works across the board amount of information is limited

    window.onerror = function(msg, url, lno){ /* No impact besides parsing overhead is only for the request to log the error when one actually happens */ return true; //hides the message in supported browsers };
  24. TRY/CATCH works across the board try { throw new Error("my

    error"); } catch (e){ e.stack; //chrome, firefox e.message; //opera log(e.stack || e.message || e); }
  25. TRY/CATCH more meaningful errors performance hit, but you should evaluate

    if it matters may lead to a lot of nesting if not thought through var fn = function(){ throw "new error"; //this exception will not be caught //since it's a string it will not have stack trace }; try{ setTimeout(fn,0); } catch(e) { }
  26. TYPES OF EXCEPTIONS Error EvalError RangeError ReferenceError SyntaxError TypeError URIError

  27. LOGGING

  28. GET VS POST both methods work fine get has the

    size of url limitation you can truncate some data (case by case evaluation)
  29. IMAGE (GET) //assuming src was already calculated var img =

    new Image(1,1); img.onload = function() {}; img.src = "/log?msg=error&url=http%3A%2F%2Flocalhost&lno=1";
  30. IFRAME (POST) var iframe = document.createElement('iframe'); document.body.appendChild(iframe); var iframeDoc =

    iframe.contentDocument, content = '<form method="post" action="/log">\ <input type="text" name="msg" value="Script Error">\ <input type="text" name="url" value="http%3A%2F%2Flocalhost">\ <input type="text" name="lno" value="1">\ </form>'; iframeDoc.open(); iframeDoc.write(content); iframeDoc.close(); iframeDoc.body.firstChild.submit();
  31. XHR (BOTH) var xhr = new XMLHttpRequest(); //post xhr.open("POST",'/log'); xhr.setRequestHeader("Content-type",

    "application/x-www-form-urlencoded"); xhr.send( payload ); //get xhr.open("GET",'/log?' + qs); xhr.send();
  32. IF YOU WANT JUST A COUNTER Google Analytics is also

    an option _gaq.push([ '_trackEvent', 'jserror', url + ':' + lno, message || '' ]);
  33. STACKTRACE.JS <script src="path/to/stacktrace.js"></script> <script> </script> //... your code ... if

    (errorCondition) { var trace = printStackTrace(); //Output however you want! alert(trace.join('\n\n')); } //... more code of yours ...
  34. STACKTRACE.JS Firefox (and Iceweasel) 0.9+ Google Chrome 1+ Safari 3.0+

    (including iOS 1+) Opera 7+ IE 5.5+ Konqueror 3.5+ Flock 1.0+ SeaMonkey 1.0+ K-Meleon 1.5.3+ Epiphany 2.28.0+ Iceape 1.1+
  35. YOUR DASHBOARD

  36. NODEJS + REDIS express node_redis npm install -g express express

    dashboard_js npm install redis
  37. EXPRESS //assuming a sample express app var log = require('./routes/log');

    app.get('/log', log.index); app.post('/log', log.index); app.get('/log_list', log.list); app.get('/log_chart', log.chart);
  38. THE ROUTERS

  39. exports.index = function (req, res) { var msg = req.param('msg',

    ''), url = req.param('url', ''), lno = req.param('lno', 0), js_error = msg + ':!:' + url + ':!:' + lno; minute = ((+new Date()/60000|0)*60000), data = [ 'jserrors', '' + minute, 1 ]; if(!msg) { res.send('', 400); } redis_cli.hincrby( data, function(){} ); redis_cli.lpush( 'jserror_' + minute, js_error); res.send('', 202); };
  40. exports.list = function(req, res) { var to = (+new Date()/60000|0)*60000,

    from = to - 20 * 60000; inc = 60000, prefix = 'jserror_', result = {}, cb = function(res, i){. return function(err, reply){ var ret = []; reply.forEach(function(val, ind){ ret[ind] = val.split(':!:'); }); result[i] = ret; if(i === to) { res.json(result); } }; }; for(var i = from; i<=to; i+=inc) { redis_cli.lrange( [prefix+i,0,100], cb(res, i) ); } };
  41. exports.chart = function(req, res) { var to = (+new Date()/60000|0)*60000,

    inc = 60000, from = to - 20 * 60000, redis_param = ['jserrors'], ret = []; for(var i = from; i<=to; i+=inc) { redis_param.push(''+i); ret.push({d: new Date(i)}); } redis_cli.hmget( redis_param, function(err, reply){ reply.forEach(function(val, ind){ ret[ind].v = val || 0; }); res.json(ret); }); };
  42. MISCELLANEOUS jquery 2.0 twitter bootstrap morris

  43. IN ACTION

  44. SERVICES OUT THERE

  45. Smart error grouping Team collaboration Easiness of integration Users statistics

    QBAKA
  46. Smart grouping of errors We don't rewrite your code Automatic

    ignoring of errors Filter errors to your liking {ERRORCEPTION}
  47. you can point it to your own service or use

    appspot more a service than a SAAS JSERRLOG
  48. Low overhead Email digest Shows the JavaScript code that caused

    the error Automatic cleanup MUSCULA
  49. SINCE WE ALREADY LOGGING Navigation Timing API not available -

    Opera, Safari
  50. PROPERTIES navigationStart unloadEventStart unloadEventEnd redirectStart redirectEnd fetchStart domainLookupStart domainLookupEnd connectStart

    connectEnd
  51. PROPERTIES secureConnectionStart requestStart responseStart responseEnd domLoading domInteractive domContentLoadedEventStart domContentLoadedEventEnd domComplete

    loadEventStart loadEventEnd
  52. VISUAL INDICATION

  53. FRONTEND SPOF any 3rd party widget custom font downloading even

    your own JS can cause it...
  54. LOG EVERYTHING YOU CAN EVERYWHERE don't expect your users to

    report your errors be aware, be prepared
  55. THANKS! Diogo Antunes Booking.com jobs

  56. Q&A

  57. None