Introduction

This post assumes that you already know what sentry is. If not following is a simply introduction.

Sentry is a error logging and aggregation platform. As it said in their document:

The Sentry package fundamentally is just a simple server and web UI. It will handle authenticating clients (such as Raven) and all of the logic behind storage and aggregation.

That said, Sentry is not limited to Python. The primary implementation is in Python, but it contains a full API for sending events from any language, in any application.

To use sentry, you can either pay for the service managed by sentry team or you can build a sentry server yourself with the open sourced code.

Preparation

Dummy Server

In order to test our notification plugin, first we will write a dummy server to receive the nofications. With python-gevent we can write one very quickly.

import gevent.server


def handle_notify(socket, address):
    """Print the nofication so we can see it."""
    print socket.recv(1024)

def main():
    server = gevent.server.StreamServer(("127.0.0.1", 9999), handle_notify)
    server.serve_forever()


if __name__ == "__main__":
    main()

The gevent.server.StreamServer will handle in-coming connections and call handle_notify for each accepted socket, concurrently. In the handle_notify function, we simply print the message sent from plugin to test if it’s alright.

Dummy Client

Besides the dummy server, we also need a dummy client to trigger the error thus send the message to sentry. Sentry support almost any language and there are many client library for us to use. Here we choose python language and raven-python and it will be very simple:

import raven

client = raven.Client(dsn=sentry_dsn)

try:
	1/0
except Exception:
	client.captureException()

The sentry_dsn will be the dsn address of your sentry server. Now when you run script by python dummy_client.py, a error message will be sent to sentry to trigger the notification.

Structure

A sentry plugin needs a specific structure to work. The sentry document has a great description so I will not elaborate it here. As an example, the notification plugin in the post has the following structure.

setup.py
sentry_notify_dummy/
sentry_notify_dummy/__init__.py
sentry_notify_dummy/plugin.py

Inside sentry_notify_dummy/plugin.py the plugin class is declared:

import socket

from sentry.plugins.bases.notify import NotificationPlugin


class NotifyDummyPlugin(NotificationPlugin):
	title = 'Dummy Notification'
	slug = 'notify-dummy'
	author = 'Kai Zhang'
	version = '0.1.0'

As you can see, sentry already has a base class for notification. It makes writing a notification plugin really easy. For now, the NotifyDummyPlugin only has some meta information like title, author and so on.

Inside of setup.py, the plugin is registered via entry_points:

#!/usr/bin/evn python
from setuptools import setup, find_packages

setup(
	name='sentry-notify-dummy',
	version='0.1.0',
	description='A sentry plugin notify the messages to the dummy server.',
	author='Kai Zhang',
	packages=find_packages(),
	install_requires=[
		'sentry',
	],
	entry_points={
		'sentry.apps': [
			'notify_dummy = sentry_notify_dummy',
		],
		'sentry.plugins': [
			'notify_dummy = sentry_notify_dummy.plugin:NotifyDummyPlugin',
		],
	},
)

Now we can install the plugin by pip install -e . inside the plugin directory. The -e argument of pip install a project in editable mode (i.e. setuptools “develop mode”) from a local project path. After this, when we restart the sentry server, it can automatically find the plugin by the entry_points. Now go to the setting page of your project in sentry, then go to the Manage Integrations page, you should see notify_dummy among all the plugins. Enable it and save the changes.

Notification

Now comes to code that actually notify the messages to our dummy server. NotificationPlugin, the class NotifyDummyPlugin is inherited from, has a method named notify_users for sub-classes to override:

	def notify_users(self, group, event, fail_silently=False):
		project_name = group.project.name
		times_seen = group.times_seen
		link = group.get_absolute_url()

		message = (
			u"{project_name} {error} ({times_seen} times seen). {link}").format(
				project_name=project_name,
				error=event.error(),
				times_seen=times_seen,
				link=link)

		connection = socket.create_connection(("localhost", 9999))
		connection.sendall(message.encode("utf-8"))

The notify_users have two important parameters, group and event. You can get the project information, how many times the message has been seen, the link of the message page in sentry and so on from group. And the message from error() of event. Then what we need to do is simply sending the message to the dummy server.

There are more information you can get from group and event. In fact, you can get group from event. You can look at sentry.models.event.Event and sentry.model.group.Group for more information.

Now, run the dummy client and you should see the notification message in dummy server.