diff --git a/django_q/cluster.py b/django_q/cluster.py
index e49792a0..23db9639 100644
--- a/django_q/cluster.py
+++ b/django_q/cluster.py
@@ -25,7 +25,7 @@
import tasks
from django_q.compat import range
-from django_q.conf import Conf, logger, psutil, get_ppid, rollbar
+from django_q.conf import Conf, logger, psutil, get_ppid, error_reporter, rollbar
from django_q.models import Task, Success, Schedule
from django_q.status import Stat, Status
from django_q.brokers import get_broker
@@ -365,6 +365,8 @@ def worker(task_queue, result_queue, timer, timeout=Conf.TIMEOUT):
f = getattr(m, func)
except (ValueError, ImportError, AttributeError) as e:
result = (e, False)
+ if error_reporter:
+ error_reporter.report()
if rollbar:
rollbar.report_exc_info()
# We're still going
@@ -380,6 +382,8 @@ def worker(task_queue, result_queue, timer, timeout=Conf.TIMEOUT):
result = (res, True)
except Exception as e:
result = ('{}'.format(e), False)
+ if error_reporter:
+ error_reporter.report()
if rollbar:
rollbar.report_exc_info()
# Process result
diff --git a/django_q/conf.py b/django_q/conf.py
index 9bfbe719..2bb6c981 100644
--- a/django_q/conf.py
+++ b/django_q/conf.py
@@ -9,6 +9,7 @@
# external
import os
+import pkg_resources
# optional
try:
@@ -141,9 +142,12 @@ class Conf(object):
# The redis stats key
Q_STAT = 'django_q:{}:cluster'.format(PREFIX)
- # Optional Rollbar key
+ # Optional rollbar key
ROLLBAR = conf.get('rollbar', {})
+ # Optional error reporting setup
+ ERROR_REPORTER = conf.get('error_reporter', {})
+
# OSX doesn't implement qsize because of missing sem_getvalue()
try:
QSIZE = Queue().qsize() == 0
@@ -177,6 +181,7 @@ class Conf(object):
handler.setFormatter(formatter)
logger.addHandler(handler)
+
# rollbar
if Conf.ROLLBAR:
rollbar_conf = deepcopy(Conf.ROLLBAR)
@@ -190,6 +195,38 @@ class Conf(object):
rollbar = None
+# Error Reporting Interface
+class ErrorReporter(object):
+
+ # initialize with iterator of reporters (better name, targets?)
+ def __init__(self, reporters):
+ self.targets = [target for target in reporters]
+
+ # report error to all configured targets
+ def report(self):
+ for t in self.targets:
+ t.report()
+
+
+# error reporting setup (sentry or rollbar)
+if Conf.ERROR_REPORTER:
+ error_conf = deepcopy(Conf.ERROR_REPORTER)
+ try:
+ reporters = []
+ # iterate through the configured error reporters,
+ # and instantiate an ErrorReporter using the provided config
+ for name, conf in error_conf.items():
+ Reporter = pkg_resources.iter_entry_points(
+ 'djangoq.errorreporters', name).load()
+ e = Reporter(**conf)
+ reporters.append(e)
+ error_reporter = ErrorReporter(reporters)
+ except ImportError:
+ error_reporter = None
+else:
+ error_reporter = None
+
+
# get parent pid compatibility
def get_ppid():
if hasattr(os, 'getppid'):
diff --git a/docs/configure.rst b/docs/configure.rst
index 08e94abf..149fb12e 100644
--- a/docs/configure.rst
+++ b/docs/configure.rst
@@ -353,6 +353,26 @@ scheduler
You can disable the scheduler by setting this option to ``False``. This will reduce a little overhead if you're not using schedules, but is most useful if you want to temporarily disable all schedules.
Defaults to ``True``
+.. _error_reporter:
+
+error_reporter
+~~~~~~~~~~~~~~
+You can redirect worker exceptions directly to various error reportes (for example, `Rollbar ` or `Sentry `) by installing Django Q with the necessary `extras `.
+
+To enable installed error reporters, you must provide the configuration settings required by an error reporter extension::
+
+ # error_reporter config--rollbar example
+ Q_CLUSTER = {
+ 'error_reporter': {
+ 'rollbar': {
+ 'access_token': '32we33a92a5224jiww8982',
+ 'environment': 'Django-Q'
+ }
+ }
+ }
+
+For more information on error reporters and developing error reporting plugins for Django Q, see :doc:`errors`.
+
rollbar
~~~~~~~
You can redirect worker exceptions directly to your `Rollbar `__ dashboard by installing the python notifier with ``pip install rollbar`` and adding this configuration dictionary to your config::
@@ -368,6 +388,10 @@ You can redirect worker exceptions directly to your `Rollbar `__ for more options.
Note that you will need a `Rollbar `__ account and access token to use this feature.
+
+.. note::
+ The ``rollbar`` setting is included for backwards compatibility, for those who utilized rollbar configuration before the ``error_reporter`` interface was introduced. Note that Rollbar support can be configured either via the ``rollbar`` setting, or via the ``django-q-rollbar`` package and enabled via the ``error_reporter`` setting above.
+
cpu_affinity
~~~~~~~~~~~~
diff --git a/docs/errors.rst b/docs/errors.rst
new file mode 100644
index 00000000..94643e20
--- /dev/null
+++ b/docs/errors.rst
@@ -0,0 +1,9 @@
+Errors
+------
+.. py:currentmodule:: django_q
+
+Django Q uses a pluggable error reporter system based upon python `extras `, allowing anyone to develop plugins for error reporting and monitoring integration. Currently implemented examples include `Rollbar ` and `Sentry `).
+
+Error reporting plugins register a class which implements a ``report`` method, which is invoked when a Django Q cluster encounters an error, pasing information to the particular service. Error reporters must be :ref:`configured` via the ``Q_CLUSTER`` dictionary in your :file:`settings.py`. These settings are passed as kwargs upon initiation of the Error Reporter. Therefore, in order to implement a new plugin, a package must expose a class which will be instantiated with the necessary information via the ``Q_CLUSTER`` settings and implements a single ``report`` method.
+
+For example implementations, see `django-q-rollbar ` and `django-q-sentry `
diff --git a/docs/index.rst b/docs/index.rst
index ee4c3f0d..c4ce6578 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -42,6 +42,7 @@ Contents:
Cluster
Monitor
Admin
+ Errors
Signals
Architecture
Examples
diff --git a/setup.py b/setup.py
index 854864a3..1c4ef11a 100644
--- a/setup.py
+++ b/setup.py
@@ -56,5 +56,15 @@ def run(self):
'Programming Language :: Python :: 3.6',
'Topic :: Internet :: WWW/HTTP',
'Topic :: Software Development :: Libraries :: Python Modules',
- ]
+ ],
+ entry_points={
+ 'djangoq.errorreporters': [
+ 'rollbar = django_q_rollbar.Rollbar',
+ 'sentry = django_q_sentry.Sentry',
+ ]
+ },
+ extras_require={
+ 'rollbar': ["django-q-rollbar>=0.1"],
+ 'sentry': ["django-q-sentry>=0.1"],
+ }
)