Phone Home
Chris Birchall
2013/08/16
#m3dev
Phone Home is...
A system to collect client-side errors
● Client
○ JavaScript
○ Runs in user’s browser
● Server
○ Scalatra
○ Accepts POST data from clients, saves to
MongoDB
https://github.com/cb372/phone-home
http://phone-home-demo.herokuapp.com/
Motivation
● Wanted to debug weird, unreproducable
jQuery errors affecting a handful of users
○ IE, of course
● Kept system running long-term to track
number and types of JS errors
● Page load timing info also useful
Error collection
Client - Features
● Error handling
○ Using either wrap() or window.onerror
● Collect page load timing info
○ Supported browsers: FF, Chrome, IE9+
● Basic logging support
○ Send arbitrary log messages to PH server
● Sampling rate
○ Avoid overloading PH server
● Custom field support
○ Attach custom key-value metadata to errors
Server - Features
● Accepts POST data from clients
○ Writes to LTSV log file
○ Stores to MongoDB
● Simple admin UI
○ Stats
○ Recent events list
● Client sample page
○ Useful for testing that POSTs are working
● CORS support (details later)
● Async event processing
○ Respond to clients as quickly as possible
XmlHttpRequest payload
{
"error": {
"line": 50,
"file": "http://phone-home-demo.herokuapp.com/sample",
"message": "fool!",
"name": "Uncaught SyntaxError"
},
"customFields": {
"baz": "hoge",
"foo": "bar"
},
"userAgent": "Mozilla/5.0 ...",
"url": "http://phone-home-demo.herokuapp.com/sample",
"app": "phone-home sample"
}
CORS - XHR
Situation:
Website and PH are on separate domains
(www.foo.com and ph.foo.com)
Problem:
Browser will not let you send an XHR to
another domain, for security reasons
Solution:
PhoneHome server supports CORS
CORS - XHR
CORS - XHR
Situation:
Website and PH are on separate domains
(www.foo.com and ph.foo.com)
Problem:
IE (< 10) doesn’t support CORS for XHR
(IE 8, 9 offer limited support using XDomainRequest)
Solution:
Proxy ph.foo.com behind www.foo.com
Proxying for IE
CORS - <script> tag
Situation:
Website and JS files are on separate domains
(www.foo.com and static.foo.com)
Problem:
Browsers hide error info for cross-origin scripts
Solution:
● Add crossorigin=”anonymous” to <script> tags
● Set up CORS support on static.foo.com webserver
○ Check request’s origin header
○ if OK, add Access-Control-Allow-Origin header
errorMessage:Script error. errorFile: errorLine:0
Thank you
Issues and pull requests welcome!
https://github.com/cb372/phone-home
http://phone-home-demo.herokuapp.com/
Bonus: Data crunching with ltsv4s
scala> val errors = io.Source.fromFile("ph-errors.log")
.getLines.map(LTSV.parseLine(_, true)).toList
errors: List[Map[String,String]] = ...
scala> errors.map(_("errorMessage")).take(2)
res1: List[String] = List(Script error., Can't find variable: Ajax)
scala> errors.count(_("userAgent").contains("Chrome"))
res2: Int = 15
libraryDependencies += "com.github.seratch" %% "ltsv4s" %
"[0.2,)"
initialCommands in console := "import com.github.seratch.ltsv4s.
_"
build.sbt

Phone Home: A client-side error collection system

  • 1.
  • 2.
    Phone Home is... Asystem to collect client-side errors ● Client ○ JavaScript ○ Runs in user’s browser ● Server ○ Scalatra ○ Accepts POST data from clients, saves to MongoDB https://github.com/cb372/phone-home http://phone-home-demo.herokuapp.com/
  • 3.
    Motivation ● Wanted todebug weird, unreproducable jQuery errors affecting a handful of users ○ IE, of course ● Kept system running long-term to track number and types of JS errors ● Page load timing info also useful
  • 4.
  • 5.
    Client - Features ●Error handling ○ Using either wrap() or window.onerror ● Collect page load timing info ○ Supported browsers: FF, Chrome, IE9+ ● Basic logging support ○ Send arbitrary log messages to PH server ● Sampling rate ○ Avoid overloading PH server ● Custom field support ○ Attach custom key-value metadata to errors
  • 6.
    Server - Features ●Accepts POST data from clients ○ Writes to LTSV log file ○ Stores to MongoDB ● Simple admin UI ○ Stats ○ Recent events list ● Client sample page ○ Useful for testing that POSTs are working ● CORS support (details later) ● Async event processing ○ Respond to clients as quickly as possible
  • 7.
    XmlHttpRequest payload { "error": { "line":50, "file": "http://phone-home-demo.herokuapp.com/sample", "message": "fool!", "name": "Uncaught SyntaxError" }, "customFields": { "baz": "hoge", "foo": "bar" }, "userAgent": "Mozilla/5.0 ...", "url": "http://phone-home-demo.herokuapp.com/sample", "app": "phone-home sample" }
  • 8.
    CORS - XHR Situation: Websiteand PH are on separate domains (www.foo.com and ph.foo.com) Problem: Browser will not let you send an XHR to another domain, for security reasons Solution: PhoneHome server supports CORS
  • 9.
  • 10.
    CORS - XHR Situation: Websiteand PH are on separate domains (www.foo.com and ph.foo.com) Problem: IE (< 10) doesn’t support CORS for XHR (IE 8, 9 offer limited support using XDomainRequest) Solution: Proxy ph.foo.com behind www.foo.com
  • 11.
  • 12.
    CORS - <script>tag Situation: Website and JS files are on separate domains (www.foo.com and static.foo.com) Problem: Browsers hide error info for cross-origin scripts Solution: ● Add crossorigin=”anonymous” to <script> tags ● Set up CORS support on static.foo.com webserver ○ Check request’s origin header ○ if OK, add Access-Control-Allow-Origin header errorMessage:Script error. errorFile: errorLine:0
  • 13.
    Thank you Issues andpull requests welcome! https://github.com/cb372/phone-home http://phone-home-demo.herokuapp.com/
  • 14.
    Bonus: Data crunchingwith ltsv4s scala> val errors = io.Source.fromFile("ph-errors.log") .getLines.map(LTSV.parseLine(_, true)).toList errors: List[Map[String,String]] = ... scala> errors.map(_("errorMessage")).take(2) res1: List[String] = List(Script error., Can't find variable: Ajax) scala> errors.count(_("userAgent").contains("Chrome")) res2: Int = 15 libraryDependencies += "com.github.seratch" %% "ltsv4s" % "[0.2,)" initialCommands in console := "import com.github.seratch.ltsv4s. _" build.sbt