Installation

For Python 2.7 compatibility, please opt for versions up to and including 4.4.0.


Raygun4py is known to work with Python 2.7, Python 3.1+, and PyPy environments.

It requires the socket module to be built with SSL support.


Grab the module with pip:

pip install raygun4py

From the command line, run:

raygun4py test paste_your_api_key_here

This will send a test exception to Raygun.


Import the module:

from raygun4py import raygunprovider

Initialize a RaygunSender:

sender = raygunprovider.RaygunSender("paste_your_api_key_here")

You may catch and manually send an exception like so:

try:
    raise Exception("Example exception")
except:
    sender.send_exception()

This must be done in the scope of an except block for the sender to obtain information about the exception via a call to sys.exc_info().

To automatically send unhandled exceptions, you can provide a callback function to sys.excepthook:

def handle_exception(exc_type, exc_value, exc_traceback):
    sender.send_exception(exc_info=(exc_type, exc_value, exc_traceback))
    sys.__excepthook__(exc_type, exc_value, exc_traceback)

sys.excepthook = handle_exception

Note that after sending the exception, we invoke the default sys.__excepthook__ to maintain the expected behavior for unhandled exceptions. This ensures the program terminates as it would without the custom exception handler in place.

You can send errors/exceptions via a logger by attaching a RaygunHandler:

logger = logging.getLogger()
raygun_handler = raygunprovider.RaygunHandler("paste_your_api_key_here")
logger.addHandler(raygun_handler)

A RaygunHandler can also be instantiated from an existing RaygunSender:

raygun_handler = raygunprovider.RaygunHandler.from_sender(sender)

It is then recommended to use logger.exception() or logger.error(exc_info=True) in the scope of an except block:

try:
    raise Exception("Example exception")
except:
    logger.exception("Example logger.exception log")
    # Or
    logger.error("Example logger.error log", exc_info=True)

Note that using a RaygunHandler outside the scope of an except block will not allow it to populate a full stack trace.


Deploy Raygun into your production environment for best results, or raise a test exception. Once we detect your first error event, the Raygun app will automatically update.


Web frameworks

Raygun4py includes dedicated middleware implementations for Django and Flask, as well as generic WSGI frameworks (Tornado, Bottle, Ginkgo, etc.). These are available for both Python 2.7 and Python 3.1+.

To configure Django to automatically send all exceptions that are raised in views to Raygun, add the following to settings.py:

MIDDLEWARE_CLASSES = (
    'raygun4py.middleware.django.Provider'
)

RAYGUN4PY_CONFIG = {
    'api_key': 'paste_your_api_key_here'
}

The above configuration is the minimal required setup. The full set of options supported by the provider can be declared in the same way:

RAYGUN4PY_CONFIG = {
    'api_key': 'paste_your_api_key_here',
    'http_timeout': 10.0,
    'proxy': None,
    'before_send_callback': None,
    'grouping_key_callback': None,
    'filtered_keys': [],
    'ignored_exceptions': [],
    'transmit_global_variables': True,
    'transmit_local_variables': True,
    'userversion': "Not defined",
    'user': None
}

To attach a request exception handler that enhances reports with Flask-specific environment data, use our middleware flask.Provider:

from flask import Flask, current_app
from raygun4py.middleware import flask

app = Flask(__name__)

flask.Provider(app, 'paste_your_api_key_here').attach()

The flask.Provider constructor can also take an optional config argument. This should be a standard Dict of supported options, as shown in Advanced configuration below. It also returns the underlying RaygunSender, which you may decide to use elsewhere.

An example using Tornado, which will pick up exceptions that occur in the WSGI pipeline:

from raygun4py.middleware import wsgi

class MainHandler(tornado.web.RequestHandler):

  def initialize(self):
      raise Exception('init')

def main():
  settings = {
      'default_handler_class': MainHandler
  }

  application = tornado.web.Application([
      (r"/", MainHandler),
  ], **settings)

  wsgiapp = tornado.wsgi.WSGIAdapter(application)
  raygun_wrapped_app = wsgi.Provider(wsgiapp, 'paste_your_api_key_here')
  server = wsgiref.simple_server.make_server('', 8888, raygun_wrapped_app)
  server.serve_forever()

The wsgi.Provider constructor can also take an optional config argument. This should be a standard Dict of supported options, as shown in Advanced configuration below.

An example using FastAPI, which will pick up exceptions that occur in a FastAPI application:

from fastapi import FastAPI, Response
from raygun4py import raygunprovider

app = FastAPI()

@app.exception_handler(Exception)
async def raygun_exception_handler(request, exc):
  client = raygunprovider.RaygunSender('paste_your_api_key_here')
  client.send_exception()
  return Response("Internal server error", status_code=500)

Note that many frameworks (Tornado, Pryramid, Gevent, etc.) will swallow exceptions that occur within their domain.

If you are in a web server environment and have HTTP request details available, you can pass these to the sender as a Dict.

Here is an example of HTTP request details structured in a Dict:

headers = {
  "referer": "localhost",
  "user-Agent": "Mozilla"
}
request = {
  "headers": headers,
  "hostName": "localhost",
  "url": "/resource",
  "httpMethod": "GET",
  "ipAddress": "127.0.0.1",
  "queryString": None,
  "form": None,
  "rawData": None,
}

sender.send_exception(request=request)

Advanced configuration

RaygunSender accepts a config Dict which is used to set options on the provider:

from raygun4py import raygunprovider

client = raygunprovider.RaygunSender('paste_your_api_key_here', config={
    'http_timeout': 10.0,
    'proxy': None,
    'before_send_callback': None,
    'grouping_key_callback': None,
    'filtered_keys': [],
    'ignored_exceptions': [],
    'transmit_global_variables': True,
    'transmit_local_variables': True,
    'userversion': "Not defined",
    'user': None
})

If 'transmit_global_variables' or 'transmit_local_variables' are set to False, the corresponding variables will not be sent with exception payloads. 'http_timeout' controls the maximum time the HTTP request can take when POSTing to the Raygun API, and is of type float.

Call this function from within an except block to send the current exception to Raygun:

send_exception(exception, exc_info, user_override, tags, userCustomData, httpRequest)
  • exception (Exception, optional): An exception instance to report.
  • exc_info (tuple, optional): A 3-tuple containing exception type, exception instance, and traceback.
  • user_override (Dict or str, optional): Information about the affected user. If not provided, self.user will be used.
  • tags (List, optional): A list of tags relating to the current context which you can define.
  • userCustomData (Dict, optional): A dictionary containing custom key-values also of your choosing.
  • httpRequest (Dict, optional): Raw HTTP request data that you wish to include with the report.

You can pass in either of these two exception parameters:

  • exception should be a subclass of type Exception. Pass this in if you want to manually transmit an Exception object to Raygun.
  • exc_info should be the 3-tuple obtained from sys.exc_info. You can pass in this tuple if you already intend to use it elsewhere.

note: If neither exception or exc_info are provided, the RaygunSender will call sys.exc_info() itself. If this occurs outside the scope of an except block, the exception information and stack trace will be missing. We recommend using a logger with a RaygunHandler attached for arbitrary error sending such as this.

  • keys (List): List of keys to filter out of the payload.

Filters sensitive data from the payload sent to Raygun. Any matching keys on the top level Raygun message object, or within dictionaries on the top level Raygun message object (including dictionaries nested within dictionaries) will have their value replaced with <filtered>. Useful for sensitive data like passwords, credit card details, etc.

sender.filtered_keys(keys=['credit_card', 'cv2', 'password'])
  • exceptions (List of strings): List of exception types to ignore.

Exceptions matching any type in this list won't be sent when passed to send_exception.

  • callback (Function callback): A function accepting one parameter.

Allows mutation of the candidate payload immediately before data is sent to Raygun.

  • callback (Function callback): A function accepting an instance of RaygunMessage.

Configures custom grouping logic and should return a string between 1 and 100 characters in length. Refer to Custom grouping logic below for more details.

  • host (str): Proxy host.
  • port (int): Proxy port.

For configurations behind a proxy, use this function to let Raygun4py route the HTTP request to the Raygun endpoint via the specified proxy.

  • version (str): SemVer version string.

Attaches a SemVer version to each message sent, visible on the dashboard, enabling exception filtering for a specific version, deployment tracking, etc.

  • user_info (Dict): User data dictionary.

User data, as displayed in the Raygun web app. The dictionary should follow the given structure:

sender.set_user({
    'firstName': 'Foo',
    'fullName': 'Foo Bar',
    'email': 'foo@bar.com',
    'isAnonymous': False,
    'identifier': 'foo@bar.com'
})

'identifier' should be a unique key used to identify users (e.g., email address). It helps in determining the count of unique affected users. If anonymity is desired, consider generating and storing a UUID or hashing one or more unique login data fields. Note: string properties on a user have a max length of 255 characters. Users exceeding this limit will not be processed.


Features

Raygun4py allows for custom exception grouping, giving you the power to override the default automatic grouping. To implement this, provide a callback function that accepts a RaygunMessage instance as its sole parameter. This instance, sourced from python[2/3]/raygunmsgs.py, contains all the error and state data set to be sent to the Raygun API.

  1. In your callback, inspect the provided RaygunMessage.
  2. Decide on the fields you want to group by and formulate a grouping key by hashing or concatenating these fields.
  3. Return this grouping key as a string from the callback.

Note: The grouping key must be between 1 to 100 characters long. If a valid key is not returned, or the callback isn't set, Raygun defaults back to its automatic grouping.

class MyClass(object):

    def my_callback(self, raygun_message):
        return raygun_message.get_error().message[:100] # Naive message-based grouping

    def create_raygun_and_bind_callback(self):
        sender = raygunprovider.RaygunSender('api_key')
        sender.on_grouping_key(self.my_callback)

In the example above, the RaygunSender utilizes the my_callback for custom grouping whenever an exception is captured. This sample logic uses only the exception message for grouping. For more complex and accurate grouping, consider a method that involves sanitizing or ignoring certain data.


The provider is open source and available at the Raygun4py repository.