Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Record.Replay.Reproduce - Dealing with JS errors in modern web apps

Record.Replay.Reproduce - Dealing with JS errors in modern web apps

#Agentconf 2017
http://agent.sh/

C3d3ad4d0758d38bf60f1a648bd0fe02?s=128

Mats Bryntse

January 21, 2017
Tweet

Transcript

  1. Mats Bryntse Founder, @bryntum Record.Replay.Reproduce Dealing with javascript errors in

    modern web apps >>
  2. Who is Mats Bryntse? • From Stockholm • Working with

    javascript past 10 years • Founder of Bryntum • Scheduler, Gantt & kanban UI components • Testing tools, logging tools • @bryntum • www.bryntum.com !
  3. Error handling 101

  4. Javascript error basics • Javascript errors are unhandled exceptions in

    your code base • Or in the frameworks you use • Doesn’t matter where errors happen, poor user impression • With JS codebases in the size of MBs, cannot ignore error handling + logging • Good news - it’s easy
  5. When a web site error happens, you as a dev

    see…
  6. What does the user see when there is a JS

    error?
  7. Nothing

  8. Two scenarios for the end user: • Error is captured

    by you. User notified • Or…… • Nothing happens for user, will probably try same action again. • If you’re lucky, user will contact you ✉ ✊
  9. Live demo of an error

  10. What is a javascript Error?

  11. The ErrorEvent constructor • When an error happens, an ´error´

    event is fired on the window object • ErrorEvent.message • ErrorEvent.filename • ErrorEvent.lineno • ErrorEvent.colno // Normal browsers only • ErrorEvent.error // Contains stack. Normal browsers only
  12. Global error handler // Old school window.onerror = function(message, source,

    lineno, colno, error) { … }; window.addEventListener(‘error’, function(event) { // event.message // event.filename // event.lineno // event.colno // good browsers only // event.error (has stack property, in good browsers) }, true); …or by listening to the window ‘error’ event. Listen with capture=true to also get notified of resource load errors Errors are easily caught in window.onerror
  13. You can throw your own Errors too // Bad, no

    call stack will be available throw ‘My own error’; // preferred throw new Error(‘This will have a call stack’); try { // Code written after drinking beer goes here } catch(e) { // Oops, log error } finally { // always called } • throw a String • Or better, an Error instance (=> callstack) • try/catch/finally
  14. Script error. • For errors originating in foreign origin scripts

    • No stack, no line, no col, no error message. Nothing. Nada. Nichts. • Solution - add a crossOrigin attribute to external script tags • And add a CORS header: Access-Control-Allow-Origin: * <script src=“https://other.io/file.js” crossOrigin="anonymous"></script>
 <script>
 window.onerror = function (message, url, line, column, error) {
 console.log(message); // Script error if you forget crossOrigin.
 }
 </script>
  15. Developer vs Bugs

  16. Bug life cycle Fix Occurrence ℹ Investigate Reproduce Report ✅

  17. Developers need to be “in context” to fix a bug

  18. Debug context wish list 1. Error message 2. File /

    line number 3. Call stack 4. Screenshot 5. Step by step description 6. Log of user / browser session activity 7. Seeing the user reproduce the error 8. Live breakpoint in production environment 9. Live breakpoint on my localhost, in my fav browser
  19. Once you have breakpoint, it’s all downhill!

  20. Error at Object.module.exports.request (/home/vagrant/src/kumascript/lib/kumascript/caching.js:366:17) at attempt (/home/vagrant/src/kumascript/lib/kumascript/loaders.js:180:24) at ks_utils.Class.get (/home/vagrant/src/kumascript/lib/kumascript/loaders.js:194:9)

    at /home/vagrant/src/kumascript/lib/kumascript/macros.js:282:24 at /home/vagrant/src/kumascript/node_modules/async/lib/async.js:118:13 at Array.forEach (native) at _each (/home/vagrant/src/kumascript/node_modules/async/lib/async.js:39:24) at Object.async.each (/home/vagrant/src/kumascript/node_modules/async/lib/async.js:117:9) at ks_utils.Class.reloadTemplates (/home/vagrant/src/kumascript/lib/kumascript/macros.js:281:19) at ks_utils.Class.process (/home/vagrant/src/kumascript/lib/kumascript/macros.js:217:15) “A live breakpoint is worth a 1000 callstacks”
  21. Common approaches to error handling

  22. • Pros: Cheap • Cons: Bugs live forever Do nothing

  23. Email ping pong

  24. Email ping pong - Enterprise version Error in web app

    Reports to own support Your company User realises it’s an error 01010 10110 11110 User Dear User, /Depressed dev. Can’t reproduce, need more info. Sincerely yours,
  25. • Pros: None • Cons: Slow, expensive, demoralizing, frustrated end

    user
  26. Roll your own logger

  27. Roll your own logger • Pros: Simple, get basic error

    info. Awareness • Cons: Lots of code to scan through
  28. Using a 3rd party logger • Pros: Tons of data,

    call stack, console logs, ajax, user activity. Enables you to search for the error • Cons: Slow, tons of data to parse, manual work, code to review
  29. Quick poll

  30. Making an error logger in < 10 minutes

  31. 1. Create single table db • date, message, file, line,

    callstack etc CREATE TABLE `error` ( `msg` char(60), `callstack` char(1000), … ) ENGINE=InnoDB DEFAULT CHARSET=utf8
  32. 2. PHP script to receive error data and store it

    in DB <?php // LOG TO DB
 $link = getLink();
 
 $command = "call insert_error('$msg', ‘$url', ‘$stack’, …); 
 $result = mysqli_query($link, $command);
 

  33. 3. Setup client side logging • Log message, file, line,

    stack etc.. • Add any extra meta relevant for your debugging (userId/name/…) // Poor mans error logger window.onerror = function log(msg) { new Image().src = "log.php?msg=" + encodeURIComponent(msg); } throw new Error("Ooops");
  34. Manual error logging, things to consider • Store error logs

    in a database on a non-production server • Throttle logging on client side + server side • Probably we only care about the first error on a page
  35. Bryntum - How we (used to) handle errors

  36. Online examples is a test suite of sorts

  37. Previous error handling at Bryntum • Web site visitors are

    test monkeys unknowingly === free help • Errors logged in a DB • Emails sent to devs = At best, useful for finding simple bugs
  38. Error handling at Bryntum Instant feedback Site visitors / “Late

    QA” Developers
  39. Error handling at Bryntum • What we had was pretty

    good, not great • Lots of time spent playing detective, looking at callstacks • Just error message, filename, callstack isn’t enough to rapidly locate root cause • We would like to know more… ,
  40. Wish list…

  41. Function arguments Know how the crashing function was called function

    getUserInfo(id) { var user = this.store.getById(id); // => null return user.getInfo(); // Cannot call getInfo of null } getUserInfo(-1); // crashes, would be neat to know input args
  42. Logs about failed ajax requests Usually produces errors that are

    less tested (aka happy testing)
  43. See how the application looked at the time of crash

    ?
  44. Know what the user did during page session

  45. Ability to replay the user session

  46. RootCause - debugging javascript errors in 2017

  47. None
  48. Callstack, browser, user environment info

  49. Capturing function arguments

  50. Screenshot at the time of the error

  51. Hiding sensitive data before screenshot

  52. + environment data collected Many things to consider… • OS

    • Browser • Window size • Touch support • Window blur/focus events • Date + Timezone • Language • Failed ajax requests • Cookie state • Network connectivity events
  53. Timeline visualising activity

  54. Notifying the developers

  55. Notifying the affected user • Optional popup for the user

    that triggered the error • Shows status of the error (New, Reproduced, Fixed)
  56. Cuts 99% of communication out • No need for QA

    / end users to email devs with crash reports, step by step • No need for devs to notify QA that bug is fixed
  57. Two way communication Error data posted Current error status [new/reproduced/fixed]

    Users / QA Developers
  58. Opt in dialog before logging

  59. Feedback button for “soft” errors

  60. Installing the logger in your web app window.logger = new

    RC.Logger({
 applicationId : 'yourToken',
 recordUserActions : true,
 showFeedbackButton : true,
 logAjaxRequests : true,
 captureScreenshot : true,
 environment : 'prod',
 user : {
 email : 'anton@aus.tirol',
 name : 'Anton'
 }
 });

  61. DEMO TIME!

  62. Technical details • Recorder: 100% vanilla JS • Screenshots: HTML2Canvas

    • Dashboard: Ext JS • Replay studio powered by Siesta
  63. >> Fast forward into context Fix Occurrence ℹ Investigate Reproduce

    Report ✅
  64. None
  65. Privacy concerns..?

  66. None
  67. BETA open at http://app.therootcause.io Summing up: • Fix your external

    script tags. Never see “Script error”. Ever. • Don’t rely on users reporting bugs • Choose an automated error reporting tool (invite code r2d2)
  68. Breakpoint or GTFO @bryntum Questions?