JavaScript Error Reporting (and Why We Can't Have Nice Things)

JavaScript Error Reporting (and Why We Can't Have Nice Things)

JSConf.uy 2016

2ab326462da24e47db9a12eb984524aa?s=128

benvinegar

April 15, 2016
Tweet

Transcript

  1. JavaScript error reporting and why we can’t have nice things

    JSConf.uy 2016
  2. @bentlegen

  3. None
  4. None
  5. None
  6. As software developers, we take great measures to ensure our

    applications are correct.
  7. Our apps still break.

  8. How do we find out?

  9. Users • Email • Complaints on social media • Bricks

    through your office window lol i can’t believe how broken
 this app is let’s shame them on the internet
  10. Server Error Logs • Server exceptions barfed to a text

    file • Apache: error.log • Commercial logging tools (Splunk, Loggly)
  11. Graphs / Monitoring • Look for spikes / drops when

    deploying
 • You must define these metrics • Looks great on TVs hung in your office
  12. Developer Console • Chrome DevTools, Safari Web Inspector, etc. •

    Console, debugger, element inspector, network inspector, more • but … needs to be open to see it
  13. We want to see JavaScript errors that occur to users

    – not us.
  14. Browser JavaScript Error Reporting +

  15. TypeError: Cannot read property ‘foo’ of undefined

  16. try / catch

  17. HTTP POST 
 “TypeError: Cannot read property ‘foo’ of undefined”

  18. try/catch (pre-2000) • No filenames, no stack trace, no line

    numbers • Does not catch syntax errors • Async callbacks means you have to write try/ catch everywhere
  19. window.onerror

  20. None
  21. function(msg, url, lineNumber)

  22. message: “TypeError: Cannot read property ‘foo’ of undefined” url: “http://example.com/foo.js”

    lineNumber: 1
  23. Your code is probably minified.

  24. Line 1

  25. Line numbers aren’t enough.

  26. Attempt #2

  27. function(msg, url, lineNumber, columnNumber)

  28. message: “TypeError: Cannot read property ‘foo’ of undefined” url: “http://example.com/foo.js”

    lineNumber: 1 columnNumber: 2380
  29. Line 1, Column 2380

  30. Better, but still not enough.

  31. We now know where the error was fired. But not

    what lead to it.
  32. Attempt #3

  33. function(msg, url, lineNumber, columnNumber, error)

  34. What’s in an Error?

  35. $ node
 > throw new Error(‘lol’)

  36. Error: lol
 at repl:1:
 at REPLServer.defaultEval (repl.js:164:27)
 at bound (domain.js:250:14)


    at REPLServer.runBound [as eval] (domain.js:263:12)
 at REPLServer.<anonymous> (repl.js:392:12)
 at emitOne (events.js:82:20)
 at REPLServer.emit (events.js:169:7)
 at REPLServer.Interface._onLine (readline.js:210:10)
 at REPLServer.Interface._line (readline.js:546:8)
 at REPLServer.Interface._ttyWrite (readline.js:823:14) throw new Error(‘lol’)
  37. Error: lol
 at repl:1:
 at REPLServer.defaultEval (repl.js:164:27)
 at bound (domain.js:250:14)


    at REPLServer.runBound [as eval] (domain.js:263:12)
 at REPLServer.<anonymous> (repl.js:392:12)
 at emitOne (events.js:82:20)
 at REPLServer.emit (events.js:169:7)
 at REPLServer.Interface._onLine (readline.js:210:10)
 at REPLServer.Interface._line (readline.js:546:8)
 at REPLServer.Interface._ttyWrite (readline.js:823:14) Name
  38. Error: lol
 at repl:1:
 at REPLServer.defaultEval (repl.js:164:27)
 at bound (domain.js:250:14)


    at REPLServer.runBound [as eval] (domain.js:263:12)
 at REPLServer.<anonymous> (repl.js:392:12)
 at emitOne (events.js:82:20)
 at REPLServer.emit (events.js:169:7)
 at REPLServer.Interface._onLine (readline.js:210:10)
 at REPLServer.Interface._line (readline.js:546:8)
 at REPLServer.Interface._ttyWrite (readline.js:823:14) Message
  39. Error: lol
 at repl:1:
 at REPLServer.defaultEval (repl.js:164:27)
 at bound (domain.js:250:14)


    at REPLServer.runBound [as eval] (domain.js:263:12)
 at REPLServer.<anonymous> (repl.js:392:12)
 at emitOne (events.js:82:20)
 at REPLServer.emit (events.js:169:7)
 at REPLServer.Interface._onLine (readline.js:210:10)
 at REPLServer.Interface._line (readline.js:546:8)
 at REPLServer.Interface._ttyWrite (readline.js:823:14) Stack
  40. Error: lol
 at repl:1:
 at REPLServer.defaultEval (repl.js:164:27)
 at bound (domain.js:250:14)


    at REPLServer.runBound [as eval] (domain.js:263:12)
 at REPLServer.<anonymous> (repl.js:392:12)
 at emitOne (events.js:82:20)
 at REPLServer.emit (events.js:169:7)
 at REPLServer.Interface._onLine (readline.js:210:10)
 at REPLServer.Interface._line (readline.js:546:8)
 at REPLServer.Interface._ttyWrite (readline.js:823:14) Frame
  41. Error: lol
 at repl:1:
 at REPLServer.defaultEval (repl.js:164:27)
 at bound (domain.js:250:14)


    at REPLServer.runBound [as eval] (domain.js:263:12)
 at REPLServer.<anonymous> (repl.js:392:12)
 at emitOne (events.js:82:20)
 at REPLServer.emit (events.js:169:7)
 at REPLServer.Interface._onLine (readline.js:210:10)
 at REPLServer.Interface._line (readline.js:546:8)
 at REPLServer.Interface._ttyWrite (readline.js:823:14) Caller
  42. Error: lol
 at repl:1:
 at REPLServer.defaultEval (repl.js:164:27)
 at bound (domain.js:250:14)


    at REPLServer.runBound [as eval] (domain.js:263:12)
 at REPLServer.<anonymous> (repl.js:392:12)
 at emitOne (events.js:82:20)
 at REPLServer.emit (events.js:169:7)
 at REPLServer.Interface._onLine (readline.js:210:10)
 at REPLServer.Interface._line (readline.js:546:8)
 at REPLServer.Interface._ttyWrite (readline.js:823:14) Source, Line Number, Column Number
  43. None
  44. But there’s a catch …

  45. > typeof new Error(‘lol').stack ‘string’

  46. > new Error('lol').stack 'Error: lol\n at repl:1:1\n at REPLServer.defaultEval (repl.js:164:27)\n

    at bound (domain.js:250:14)\n at REPLServer.runBound [as eval] (domain.js: 263:12)\n at REPLServer.<anonymous> (repl.js:392:12)\n at emitOne (events.js:82:20)\n at REPLServer.emit (events.js:169:7)\n at REPLServer.Interface._onLine (readline.js: 210:10)\n at REPLServer.Interface._line (readline.js:546:8)\n at REPLServer.Interface._ttyWrite (readline.js:823:14)'
  47. Error.prototype.stack is not standardized.

  48. None
  49. Chrome 49 identifies bar as an object
 property of MyClass

    cannot identify functions
 assigned to variables inconsistent parens
  50. Firefox 44 different line number? identifies bar as a prototype


    method of MyClass has variable name of
 anonymous function 
 (what’s ‘<‘?)
  51. Safari 9 does not identify bar as a
 property of

    MyClass identifies code executed 
 in global scope more line number
 changes
  52. Every browser interprets and formats Error.prototype.stack differently.

  53. Good news: libraries.

  54. None
  55. None
  56. None
  57. One more catch …

  58. window.onerror does not always support an error argument

  59. window.onerror compatibility Line Column Error (Stack) Chrome ✓ ✓ ✓

    Firefox ✓ ✓ ✓ Safari 9 ✓ ✓ Edge ✓ ✓ IE 11+ ✓ ✓ ✓ IE 10 ✓ ✓ IE 8, 9 ✓
  60. None
  61. Back to try/catch

  62. None
  63. If you want stack traces in every browser, you must

    try/ catch everything anyways.
  64. None
  65. Basically this is a nightmare.

  66. So, what was the point of all this?

  67. You absolutely should collect JavaScript errors in production.

  68. Easy to get a lot of value with window.onerror +

    libraries + reporting endpoint.
  69. Difficult to get full context in every browser – try/catch

    all your code.
  70. Consider using an off-the- shelf solution that does everything.

  71. None
  72. More stuff • Thanks to Matt Robenolt and the Sentry

    team for helping me with these slides • Stacktrace.js: https://stacktracejs.com • TraceKit: https://github.com/csnover/tracekit • me: @bentlegen • Sentry plug: https://getsentry.com