From 9e97d48012cea235b3cae6b7b1fc061612e85c48 Mon Sep 17 00:00:00 2001 From: Daniel Welch Date: Thu, 30 Mar 2017 21:51:57 -0400 Subject: [PATCH 1/6] registering entry points for sentry and rollbar plugins, maintained in seperate repos. start of a generic error handling implementation --- django_q/conf.py | 52 ++++++++++++++++++++++++++++++++++++++---------- setup.py | 12 ++++++++++- 2 files changed, 53 insertions(+), 11 deletions(-) diff --git a/django_q/conf.py b/django_q/conf.py index 9bfbe719..9dffaf50 100644 --- a/django_q/conf.py +++ b/django_q/conf.py @@ -9,6 +9,7 @@ # external import os +import pkg_resources # optional try: @@ -141,8 +142,8 @@ class Conf(object): # The redis stats key Q_STAT = 'django_q:{}:cluster'.format(PREFIX) - # 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: @@ -177,17 +178,48 @@ class Conf(object): handler.setFormatter(formatter) logger.addHandler(handler) -# rollbar -if Conf.ROLLBAR: - rollbar_conf = deepcopy(Conf.ROLLBAR) + +# 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: - import rollbar - rollbar.init(rollbar_conf.pop('access_token'), environment=rollbar_conf.pop('environment'), **rollbar_conf) + 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: - rollbar = None + error_reporter = None + +# # rollbar +# if Conf.ROLLBAR: +# rollbar_conf = deepcopy(Conf.ROLLBAR) +# try: +# import rollbar +# rollbar.init(rollbar_conf.pop('access_token'), environment=rollbar_conf.pop('environment'), **rollbar_conf) +# except ImportError: +# rollbar = None + +# else: +# rollbar = None -else: - rollbar = None # get parent pid compatibility diff --git a/setup.py b/setup.py index ee94ecb7..2be17c7c 100644 --- a/setup.py +++ b/setup.py @@ -55,5 +55,15 @@ def run(self): 'Programming Language :: Python :: 3.5', '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"], + } ) From aac6984ec3d100f604eccaa9c4a9ee612280f392 Mon Sep 17 00:00:00 2001 From: Daniel Welch Date: Thu, 30 Mar 2017 22:02:31 -0400 Subject: [PATCH 2/6] replace rollbar reporting with generic error_reporter reporting in cluster.py --- django_q/cluster.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/django_q/cluster.py b/django_q/cluster.py index 3810b155..ba50bcc4 100644 --- a/django_q/cluster.py +++ b/django_q/cluster.py @@ -30,7 +30,7 @@ import signing import tasks -from django_q.conf import Conf, logger, psutil, get_ppid, rollbar +from django_q.conf import Conf, logger, psutil, get_ppid, error_reporter from django_q.models import Task, Success, Schedule from django_q.status import Stat, Status from django_q.brokers import get_broker @@ -368,8 +368,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 rollbar: - rollbar.report_exc_info() + if error_reporter: + error_reporter.report() # We're still going if not result: db.close_old_connections() @@ -380,8 +380,8 @@ def worker(task_queue, result_queue, timer, timeout=Conf.TIMEOUT): result = (res, True) except Exception as e: result = ('{}'.format(e), False) - if rollbar: - rollbar.report_exc_info() + if error_reporter: + error_reporter.report() # Process result task['result'] = result[0] task['success'] = result[1] From c82ef6a1770827fc791900cf35fd4617b185000f Mon Sep 17 00:00:00 2001 From: Daniel Welch Date: Thu, 30 Mar 2017 22:12:31 -0400 Subject: [PATCH 3/6] fixing export from conf --- django_q/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/django_q/conf.py b/django_q/conf.py index 9dffaf50..bf62a17a 100644 --- a/django_q/conf.py +++ b/django_q/conf.py @@ -207,6 +207,8 @@ def report(self): error_reporter = ErrorReporter(reporters) except ImportError: error_reporter = None +else: + error_reporter = None # # rollbar # if Conf.ROLLBAR: From f5e62bb624f3654b1de259031c91854b88dcce21 Mon Sep 17 00:00:00 2001 From: Daniel Welch Date: Tue, 3 Oct 2017 17:04:37 -0400 Subject: [PATCH 4/6] documentation for error reporter plugin system --- docs/configure.rst | 40 ++++++++++++++++++++++++++++++---------- docs/errors.rst | 9 +++++++++ docs/index.rst | 1 + 3 files changed, 40 insertions(+), 10 deletions(-) create mode 100644 docs/errors.rst diff --git a/docs/configure.rst b/docs/configure.rst index 08e94abf..3091ca62 100644 --- a/docs/configure.rst +++ b/docs/configure.rst @@ -353,20 +353,40 @@ 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`` -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:: +.. _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 `. - # rollbar config +To enable installed error reporters, you must provide the configuration settings required by an error reporter extension:: + + # error_reporter config--rollbar example Q_CLUSTER = { - 'rollbar': { - 'access_token': '32we33a92a5224jiww8982', - 'environment': 'Django-Q' - } + 'error_reporter': { + 'rollbar': { + 'access_token': '32we33a92a5224jiww8982', + 'environment': 'Django-Q' + } + } } -Please check the Pyrollbar `configuration reference `__ for more options. -Note that you will need a `Rollbar `__ account and access token to use this feature. +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:: + +.. # rollbar config +.. Q_CLUSTER = { +.. 'rollbar': { +.. 'access_token': '32we33a92a5224jiww8982', +.. 'environment': 'Django-Q' +.. } +.. } + +.. Please check the Pyrollbar `configuration reference `__ for more options. +.. Note that you will need a `Rollbar `__ account and access token to use this feature. 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 From cd038f6f615dc584b44d528428d21c1c8026369c Mon Sep 17 00:00:00 2001 From: Daniel Welch Date: Tue, 3 Oct 2017 17:08:26 -0400 Subject: [PATCH 5/6] remove old rollbar stuff from config --- django_q/conf.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/django_q/conf.py b/django_q/conf.py index bf62a17a..093e8e57 100644 --- a/django_q/conf.py +++ b/django_q/conf.py @@ -210,19 +210,6 @@ def report(self): else: error_reporter = None -# # rollbar -# if Conf.ROLLBAR: -# rollbar_conf = deepcopy(Conf.ROLLBAR) -# try: -# import rollbar -# rollbar.init(rollbar_conf.pop('access_token'), environment=rollbar_conf.pop('environment'), **rollbar_conf) -# except ImportError: -# rollbar = None - -# else: -# rollbar = None - - # get parent pid compatibility def get_ppid(): From 4530b05d86a05811f3f34a325d8598be4579fd62 Mon Sep 17 00:00:00 2001 From: Daniel Welch Date: Fri, 6 Oct 2017 13:26:43 -0400 Subject: [PATCH 6/6] putting back support for backwards compatibility --- django_q/cluster.py | 6 +++++- django_q/conf.py | 16 ++++++++++++++++ docs/configure.rst | 32 ++++++++++++++++++-------------- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/django_q/cluster.py b/django_q/cluster.py index 32935a47..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, error_reporter +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 @@ -367,6 +367,8 @@ def worker(task_queue, result_queue, timer, timeout=Conf.TIMEOUT): result = (e, False) if error_reporter: error_reporter.report() + if rollbar: + rollbar.report_exc_info() # We're still going if not result: db.close_old_connections() @@ -382,6 +384,8 @@ def worker(task_queue, result_queue, timer, timeout=Conf.TIMEOUT): result = ('{}'.format(e), False) if error_reporter: error_reporter.report() + if rollbar: + rollbar.report_exc_info() # Process result task['result'] = result[0] task['success'] = result[1] diff --git a/django_q/conf.py b/django_q/conf.py index 093e8e57..2bb6c981 100644 --- a/django_q/conf.py +++ b/django_q/conf.py @@ -142,6 +142,9 @@ class Conf(object): # The redis stats key Q_STAT = 'django_q:{}:cluster'.format(PREFIX) + # Optional rollbar key + ROLLBAR = conf.get('rollbar', {}) + # Optional error reporting setup ERROR_REPORTER = conf.get('error_reporter', {}) @@ -179,6 +182,19 @@ class Conf(object): logger.addHandler(handler) +# rollbar +if Conf.ROLLBAR: + rollbar_conf = deepcopy(Conf.ROLLBAR) + try: + import rollbar + rollbar.init(rollbar_conf.pop('access_token'), environment=rollbar_conf.pop('environment'), **rollbar_conf) + except ImportError: + rollbar = None + +else: + rollbar = None + + # Error Reporting Interface class ErrorReporter(object): diff --git a/docs/configure.rst b/docs/configure.rst index 3091ca62..149fb12e 100644 --- a/docs/configure.rst +++ b/docs/configure.rst @@ -373,20 +373,24 @@ To enable installed error reporters, you must provide the configuration settings 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:: - -.. # rollbar config -.. Q_CLUSTER = { -.. 'rollbar': { -.. 'access_token': '32we33a92a5224jiww8982', -.. 'environment': 'Django-Q' -.. } -.. } - -.. Please check the Pyrollbar `configuration reference `__ for more options. -.. Note that you will need a `Rollbar `__ account and access token to use this feature. +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:: + + # rollbar config + Q_CLUSTER = { + 'rollbar': { + 'access_token': '32we33a92a5224jiww8982', + 'environment': 'Django-Q' + } + } + +Please check the Pyrollbar `configuration reference `__ 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 ~~~~~~~~~~~~