JavaScript error reporting
and why we can’t have nice
things
JSConf.uy 2016
Slide 2
Slide 2 text
@bentlegen
Slide 3
Slide 3 text
No content
Slide 4
Slide 4 text
No content
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
As software developers, we
take great measures to ensure
our applications are correct.
Slide 7
Slide 7 text
Our apps still break.
Slide 8
Slide 8 text
How do we find out?
Slide 9
Slide 9 text
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
Slide 10
Slide 10 text
Server Error Logs
• Server exceptions
barfed to a text file
• Apache: error.log
• Commercial logging
tools (Splunk, Loggly)
Slide 11
Slide 11 text
Graphs / Monitoring
• Look for spikes / drops
when deploying
• You must define these
metrics
• Looks great on TVs
hung in your office
Slide 12
Slide 12 text
Developer Console
• Chrome DevTools, Safari
Web Inspector, etc.
• Console, debugger,
element inspector,
network inspector, more
• but … needs to be open
to see it
Slide 13
Slide 13 text
We want to see JavaScript
errors that occur to users –
not us.
Slide 14
Slide 14 text
Browser JavaScript Error Reporting
+
Slide 15
Slide 15 text
TypeError: Cannot read property ‘foo’ of undefined
Slide 16
Slide 16 text
try / catch
Slide 17
Slide 17 text
HTTP POST
“TypeError: Cannot read property ‘foo’ of undefined”
Slide 18
Slide 18 text
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
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. (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’)
Slide 37
Slide 37 text
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. (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
Slide 38
Slide 38 text
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. (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
Slide 39
Slide 39 text
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. (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
Slide 40
Slide 40 text
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. (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
Slide 41
Slide 41 text
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. (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
Slide 42
Slide 42 text
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. (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
Slide 43
Slide 43 text
No content
Slide 44
Slide 44 text
But there’s a catch …
Slide 45
Slide 45 text
> typeof new Error(‘lol').stack
‘string’
Slide 46
Slide 46 text
> 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.
(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)'
Slide 47
Slide 47 text
Error.prototype.stack
is not standardized.
Slide 48
Slide 48 text
No content
Slide 49
Slide 49 text
Chrome 49
identifies bar as an object
property of MyClass
cannot identify functions
assigned to variables
inconsistent parens
Slide 50
Slide 50 text
Firefox 44
different line number?
identifies bar as a prototype
method of MyClass
has variable name of
anonymous function
(what’s ‘<‘?)
Slide 51
Slide 51 text
Safari 9
does not identify bar as a
property of MyClass
identifies code executed
in global scope
more line number
changes
Slide 52
Slide 52 text
Every browser interprets and
formats Error.prototype.stack
differently.
Slide 53
Slide 53 text
Good news: libraries.
Slide 54
Slide 54 text
No content
Slide 55
Slide 55 text
No content
Slide 56
Slide 56 text
No content
Slide 57
Slide 57 text
One more catch …
Slide 58
Slide 58 text
window.onerror does not
always support an error
argument
If you want stack traces in
every browser, you must try/
catch everything anyways.
Slide 64
Slide 64 text
No content
Slide 65
Slide 65 text
Basically this is a
nightmare.
Slide 66
Slide 66 text
So, what was the
point of all this?
Slide 67
Slide 67 text
You absolutely should collect
JavaScript errors in
production.
Slide 68
Slide 68 text
Easy to get a lot of value with
window.onerror + libraries +
reporting endpoint.
Slide 69
Slide 69 text
Difficult to get full context in
every browser – try/catch all
your code.
Slide 70
Slide 70 text
Consider using an off-the-
shelf solution that does
everything.
Slide 71
Slide 71 text
No content
Slide 72
Slide 72 text
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