Posted on July 30, 2014 by

Log Errors to Github

Log files are great, but why not make errors front and center in your issue tracker. One thing I like to do is automatically log errors to Github. That way when something goes wrong, it is right in my face.

class GithubError(logging.Handler):
    def __init__(self, token, repo_owner, repo):
        logging.Handler.__init__(self)
        self.token = token
        self.repo_owner = repo_owner
        self.repo = repo
 
    def log_issue(self, title, body):
        from github3 import login
 
        gh = login(token=self.token)
        if gh is None:
            app.logger.warning('Could not login to Github!')
            return
 
        repo = gh.repository(self.repo_owner, self.repo)
 
        try:
            issue = repo.create_issue(title, unicode(body), self.repo_owner)
        except Exception as e:
            app.logger.warning(e)
 
    def emit(self, record):
        import traceback
 
        title = 'Auto-reported Bug'
        if record.exc_info is not None:
            # If we are passed traceback information, format it nicely
            tb = traceback.format_tb(record.exc_info[2])
            body = unicode('\n'.join(tb))
        else:
            # Otherwise just include the error and some meta
            body = u'{}:{}\n{}'.format(record.pathname, record.lineno, record.msg)
 
        self.log_issue(title, body)

This class extends logging.Handler, which lets use it as a handler in Flask’s logging system. The emit method is passed a logRecord object that can be examined for useful information to pass to Github.

To hook the GithubError class into Flask’s logger, you want to run some code before the first request.

@app.before_first_request
def setup_logging():
    if not app.debug:
      import logging
      from logging import StreamHandler
 
      handler = StreamHandler(sys.stdout)
      app.logger.addHandler(handler)
      app.logger.setLevel(logging.INFO)
 
      if app.config['GITHUB_USER'] is not None and app.config['GITHUB_PASS'] is not None:
          gh_handler = GithubError(app.config['GITHUB_TOKEN'],
              app.config['GITHUB_USER'],
              app.config['GITHUB_REPO'])
          gh_handler.setLevel(logging.ERROR)
          app.logger.addHandler(gh_handler)

If Flask is running in debug mode, then there is no reason to send anything off to Github. This is really for production mode only.