Writing a Hoptoad Notifier: Contacting the Toad

Mike Burns

… in Python.

Hoptoad, our app error app, is a Web service that accepts XML. No Rails-specific tricks going on here; you can have runtime errors in any programming language and Hoptoad can handle that for you.

Let’s build a notifier in some language I don’t know. How about Python?

The main step is to actually send the data to Hoptoad. Subsequent steps (integration testing, plugins for your framework, and so on) are either outside the scope of this tutorial or may be covered in a future tutorial.

Sending the data is two parts: generating the data to send, then sending it. To generate the data we can use any library that will produce XML for us.

Generating the data

import traceback
import sys
import urllib2
import xmlbuilder

from xmlbuilder import XMLBuilder

def api_key():
  return "YOUR API KEY"

def generate_xml(exn):
  _,_,trace = sys.exc_info()

  xml = XMLBuilder()
  with xml.notice(version = 2.0):
    xml << ('api-key', api_key())
    with xml.notifier:
      xml << ('name', 'Dennis')
      xml << ('version', '0.1')
      xml << ('url', 'http://hoptoadapp.com')
    with xml('server-environment'):
      xml << ('environment-name', 'REPL')
    with xml.error:
      xml << ('class', exn.__class__.__name__)
      xml << ('message', str(exn))
      with xml.backtrace:
        [xml << ('line', {'file':filename, 'number':line_number, 'method':function_name})
            for filename, line_number, function_name, _ in traceback.extract_tb(trace)]

  return str(xml)

What we’ve written here is a method that takes an exception and produces the XML that should be sent to Hoptoad. Nothing especially tricky happens here; we pull out the class of exception and the message describing it, then grab and generate the XML for the backtrace.

Next up: sending this data.

Sending this data

Sending the data isn’t too tricky. It’s a HTTP POST with the text/xml content type to /notifier_api/v2/notices. The only edge case is that paid accounts support SSL.

def hoptoad_url(use_ssl):
  if use_ssl:
    return "https://hoptoadapp.com/notifier_api/v2/notices"
  else:
    return "http://hoptoadapp.com/notifier_api/v2/notices"

def send_to_hoptoad(xml, use_ssl):
  headers = { 'Content-Type': 'text/xml' }
  request = urllib2.Request(hoptoad_url(use_ssl), xml, headers)
  response = urllib2.urlopen(request)
  status = response.getcode()
  if status == 200:
    pass
  if status == 403:
    raise Exception("Cannot use SSL")
  if status == 422:
    raise Exception("Invalid XML sent to Hoptoad")
  if status == 500:
    raise Exception("Hoptoad has hopped the toad")

def notify_hoptoad(exn, use_ssl):
  send_to_hoptoad(generate_xml(exn), use_ssl)

The HTTP response indicates how well it worked; the exceptional cases are 403 (the customer does not have a Hoptoad account that supports SSL), 422 (invalid XML), and 500 (server error).

So that’s it:

try:
  1/0
except ZeroDivisionError as exn:
  notify_hoptoad(exn, True)

That is what is needed to send an exception to Hoptoad from Python. To use Hoptoad with Django there is the full django-hoptoad plugin; a full list of alternative plugins exists too. Perhaps yours can be there soon too!

FYI: Hoptoad/Airbrake was sold to RackSpace and is now called Airbrake Bug Tracker.