diff options
| author | Xavier Ordoquy <xordoquy@linovia.com> | 2015-03-01 13:15:59 +0100 |
|---|---|---|
| committer | Xavier Ordoquy <xordoquy@linovia.com> | 2015-03-01 13:15:59 +0100 |
| commit | b58cd382366c5487cf6fb8ec956006eff4a9a87a (patch) | |
| tree | 63d03f99ee69ae5b54fa7fa74b6640da1d9e9a64 | |
| parent | 49e5ee859bf1c126f8353a3d75d50fbe08463f89 (diff) | |
| parent | c29dbde03b4ee9df47169210da00333d7290d262 (diff) | |
| download | raven-feature/django_removal.tar.gz | |
Merge remote-tracking branch 'origin/master' into feature/django_removalfeature/django_removal
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | .travis.yml | 8 | ||||
| -rw-r--r-- | CHANGES | 2 | ||||
| -rw-r--r-- | MANIFEST.in | 1 | ||||
| -rw-r--r-- | conftest.py | 1 | ||||
| -rw-r--r-- | docs/config/index.rst | 4 | ||||
| -rw-r--r-- | docs/integrations/celery.rst | 3 | ||||
| -rw-r--r-- | docs/integrations/django.rst | 12 | ||||
| -rw-r--r-- | raven/base.py | 34 | ||||
| -rw-r--r-- | raven/contrib/celery/__init__.py | 4 | ||||
| -rw-r--r-- | raven/contrib/django/management/__init__.py | 4 | ||||
| -rw-r--r-- | raven/contrib/django/models.py | 7 | ||||
| -rw-r--r-- | raven/contrib/zope/__init__.py | 8 | ||||
| -rw-r--r-- | raven/handlers/logging.py | 4 | ||||
| -rw-r--r-- | raven/processors.py | 9 | ||||
| -rw-r--r-- | raven/utils/compat.py | 5 | ||||
| -rw-r--r-- | raven/utils/testutils.py | 5 | ||||
| -rw-r--r-- | raven/versioning.py | 8 | ||||
| -rwxr-xr-x | setup.py | 8 | ||||
| -rw-r--r-- | tests/functional/tests.py | 2 | ||||
| -rw-r--r-- | tests/processors/tests.py | 18 | ||||
| -rw-r--r-- | tox.ini | 4 |
22 files changed, 110 insertions, 42 deletions
@@ -22,3 +22,4 @@ bin/ include/ lib/ .idea +.eggs diff --git a/.travis.yml b/.travis.yml index 9876009..1714c3e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ env: - DJANGO=Django==1.4.18 - DJANGO=Django==1.5.10 - DJANGO=Django==1.6.10 - - DJANGO=Django==1.7.3 - - DJANGO=Django==1.8a1 + - DJANGO=Django==1.7.5 + - DJANGO=Django==1.8b1 - 'DJANGO="-e git+git://github.com/django/django.git#egg=Django"' global: - 'PIP_DOWNLOAD_CACHE=".pip_download_cache"' @@ -56,6 +56,6 @@ matrix: - python: "2.6" env: DJANGO="-e git+git://github.com/django/django.git#egg=Django" - python: "2.6" - env: DJANGO=Django==1.8a1 + env: DJANGO=Django==1.8b1 - python: "2.6" - env: DJANGO=Django==1.7.3 + env: DJANGO=Django==1.7.5 @@ -5,6 +5,8 @@ Version 5.2.0 * Added ``release`` option to Client. * Added ``fetch_git_sha`` helper. * Added ``fetch_package_version`` helper. +* Added cookie string sanatizing. +* Added threaded request transport: "threaded+requests+http(s)". Version 5.1.0 ------------- diff --git a/MANIFEST.in b/MANIFEST.in index 1d6d061..76ddd09 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include setup.py README.rst MANIFEST.in LICENSE *.txt recursive-include raven/contrib/zope *.xml recursive-include raven/data * +graft tests global-exclude *~ diff --git a/conftest.py b/conftest.py index b5b6aa1..f333890 100644 --- a/conftest.py +++ b/conftest.py @@ -30,6 +30,7 @@ INSTALLED_APPS = [ 'django.contrib.contenttypes', 'raven.contrib.django', + 'tests.contrib.django', ] diff --git a/docs/config/index.rst b/docs/config/index.rst index a409110..0679abd 100644 --- a/docs/config/index.rst +++ b/docs/config/index.rst @@ -233,8 +233,8 @@ Several processors are included with Raven to assist in data sanitiziation. Thes .. data:: raven.processors.SanitizePasswordsProcessor Removes all keys which resemble ``password``, ``secret``, or ``api_key`` - within stacktrace contexts and HTTP bits (such as cookies, POST data, - the querystring, and environment). + within stacktrace contexts, HTTP bits (such as cookies, POST data, + the querystring, and environment), and extra data. .. data:: raven.processors.RemoveStackLocalsProcessor diff --git a/docs/integrations/celery.rst b/docs/integrations/celery.rst index d854f6d..7fa6117 100644 --- a/docs/integrations/celery.rst +++ b/docs/integrations/celery.rst @@ -16,6 +16,9 @@ tl;dr register a couple of signals to hijack Celery error handling # hook into the Celery error handler register_signal(client) + # The register_signal function can also take an optional argument `loglevel` + # which is the level used for the handler created. Defaults to `logging.ERROR` + register_signal(client, loglevel=logging.INFO) A more complex version to encapsulate behavior: diff --git a/docs/integrations/django.rst b/docs/integrations/django.rst index fa1c8a9..96ea0e8 100644 --- a/docs/integrations/django.rst +++ b/docs/integrations/django.rst @@ -206,6 +206,18 @@ client:: SENTRY_CLIENT = 'raven.contrib.django.raven_compat.DjangoClient' +SENTRY_CELERY_LOGLEVEL +~~~~~~~~~~~~~~~~~~~~~~ + +If you are also using Celery, there is a handler being automatically registered +for you that captures the errors from workers. The default logging level for +that handler is ``logging.ERROR`` and can be customized using this setting:: + + SENTRY_CELERY_LOGLEVEL = logging.INFO + RAVEN_CONFIG = { + 'CELERY_LOGLEVEL': logging.INFO + } + Caveats ------- diff --git a/raven/base.py b/raven/base.py index ec93e60..a01743f 100644 --- a/raven/base.py +++ b/raven/base.py @@ -328,24 +328,22 @@ class Client(object): 'stacktrace': stack_info, }) - if 'stacktrace' in data: - if self.include_paths: - for frame in data['stacktrace']['frames']: - if frame.get('in_app') is not None: - continue - - path = frame.get('module') - if not path: - continue - - if path.startswith('raven.'): - frame['in_app'] = False - else: - frame['in_app'] = ( - any(path.startswith(x) for x in self.include_paths) - and not - any(path.startswith(x) for x in self.exclude_paths) - ) + if 'stacktrace' in data and self.include_paths: + for frame in data['stacktrace']['frames']: + if frame.get('in_app') is not None: + continue + + path = frame.get('module') + if not path: + continue + + if path.startswith('raven.'): + frame['in_app'] = False + else: + frame['in_app'] = ( + any(path.startswith(x) for x in self.include_paths) and + not any(path.startswith(x) for x in self.exclude_paths) + ) if not culprit: if 'stacktrace' in data: diff --git a/raven/contrib/celery/__init__.py b/raven/contrib/celery/__init__.py index fefec6c..56b7247 100644 --- a/raven/contrib/celery/__init__.py +++ b/raven/contrib/celery/__init__.py @@ -36,13 +36,13 @@ def register_signal(client): task_failure.connect(process_failure_signal, weak=False) -def register_logger_signal(client, logger=None): +def register_logger_signal(client, logger=None, loglevel=logging.ERROR): filter_ = CeleryFilter() if logger is None: logger = logging.getLogger() handler = SentryHandler(client) - handler.setLevel(logging.ERROR) + handler.setLevel(loglevel) handler.addFilter(filter_) def process_logger_event(sender, logger, loglevel, logfile, format, diff --git a/raven/contrib/django/management/__init__.py b/raven/contrib/django/management/__init__.py index b4615c2..d1f1fe5 100644 --- a/raven/contrib/django/management/__init__.py +++ b/raven/contrib/django/management/__init__.py @@ -50,8 +50,8 @@ def patch_base_command(cls): return True -if ('raven.contrib.django' in settings.INSTALLED_APPS - or 'raven.contrib.django.raven_compat' in settings.INSTALLED_APPS): +if ('raven.contrib.django' in settings.INSTALLED_APPS or + 'raven.contrib.django.raven_compat' in settings.INSTALLED_APPS): try: from django.core.management.base import BaseCommand diff --git a/raven/contrib/django/models.py b/raven/contrib/django/models.py index 04bf8fc..c343072 100644 --- a/raven/contrib/django/models.py +++ b/raven/contrib/django/models.py @@ -221,7 +221,12 @@ def register_handlers(): logger.exception('Failed to install Celery error handler') try: - register_logger_signal(client) + ga = lambda x, d=None: getattr(django_settings, 'SENTRY_%s' % x, d) + options = getattr(django_settings, 'RAVEN_CONFIG', {}) + loglevel = options.get('celery_loglevel', + ga('CELERY_LOGLEVEL', logging.ERROR)) + + register_logger_signal(client, loglevel=loglevel) except Exception: logger.exception('Failed to install Celery error handler') diff --git a/raven/contrib/zope/__init__.py b/raven/contrib/zope/__init__.py index ca079c4..1ece309 100644 --- a/raven/contrib/zope/__init__.py +++ b/raven/contrib/zope/__init__.py @@ -45,8 +45,14 @@ class ZopeSentryHandler(SentryHandler): level = kw.get('level', logging.ERROR) self.setLevel(level) + def can_record(self, record): + return not ( + record.name == 'raven' or + record.name.startswith(('sentry.errors', 'raven.')) + ) + def emit(self, record): - if record.levelno <= logging.ERROR: + if record.levelno <= logging.ERROR and self.can_record(record): request = None exc_info = None for frame_info in getouterframes(currentframe()): diff --git a/raven/handlers/logging.py b/raven/handlers/logging.py index bf802ba..e908399 100644 --- a/raven/handlers/logging.py +++ b/raven/handlers/logging.py @@ -89,8 +89,8 @@ class SentryHandler(logging.Handler, object): if not started: f_globals = getattr(frame, 'f_globals', {}) module_name = f_globals.get('__name__', '') - if ((last_mod and last_mod.startswith('logging')) - and not module_name.startswith('logging')): + if ((last_mod and last_mod.startswith('logging')) and + not module_name.startswith('logging')): started = True else: last_mod = module_name diff --git a/raven/processors.py b/raven/processors.py index ce5e113..adf60e9 100644 --- a/raven/processors.py +++ b/raven/processors.py @@ -34,6 +34,9 @@ class Processor(object): if 'request' in data: self.filter_http(data['request']) + if 'extra' in data: + data['extra'] = self.filter_extra(data['extra']) + return data def filter_stacktrace(self, data): @@ -42,6 +45,9 @@ class Processor(object): def filter_http(self, data): pass + def filter_extra(self, data): + return data + class RemovePostDataProcessor(Processor): """ @@ -115,6 +121,9 @@ class SanitizePasswordsProcessor(Processor): data[n]['Cookie'], ';' ) + def filter_extra(self, data): + return varmap(self.sanitize, data) + def _sanitize_keyvals(self, keyvals, delimiter): sanitized_keyvals = [] for keyval in keyvals.split(delimiter): diff --git a/raven/utils/compat.py b/raven/utils/compat.py index f1af91d..e00b499 100644 --- a/raven/utils/compat.py +++ b/raven/utils/compat.py @@ -46,8 +46,3 @@ except ImportError: from urllib import parse as _urlparse # NOQA urlparse = _urlparse - -try: - from unittest2 import TestCase -except ImportError: - from unittest import TestCase # NOQA diff --git a/raven/utils/testutils.py b/raven/utils/testutils.py index 6be28b5..52df1b1 100644 --- a/raven/utils/testutils.py +++ b/raven/utils/testutils.py @@ -9,7 +9,10 @@ from __future__ import absolute_import from exam import Exam -from .compat import TestCase as BaseTestCase +try: + from unittest2 import TestCase as BaseTestCase +except ImportError: + from unittest import TestCase as BaseTestCase # NOQA class TestCase(Exam, BaseTestCase): diff --git a/raven/versioning.py b/raven/versioning.py index e697c0b..616efd8 100644 --- a/raven/versioning.py +++ b/raven/versioning.py @@ -1,7 +1,11 @@ from __future__ import absolute_import import os.path -import pkg_resources +try: + import pkg_resources +except ImportError: + # pkg_resource is not available on Google App Engine + pkg_resources = None from .exceptions import InvalidGitRepository @@ -29,5 +33,7 @@ def fetch_package_version(dist_name): """ >>> fetch_package_version('sentry') """ + if pkg_resources is None: + raise NotImplementedError('pkg_resources is not available on this Python install') dist = pkg_resources.get_distribution(dist_name) return dist.version @@ -82,14 +82,20 @@ tests_require = [ class PyTest(TestCommand): + + def initialize_options(self): + TestCommand.initialize_options(self) + self.pytest_args = [] + def finalize_options(self): TestCommand.finalize_options(self) + self.test_args = [] self.test_suite = True def run_tests(self): # import here, cause outside the eggs aren't loaded import pytest - errno = pytest.main(self.test_args) + errno = pytest.main(self.pytest_args) sys.exit(errno) diff --git a/tests/functional/tests.py b/tests/functional/tests.py index 80996b6..7255414 100644 --- a/tests/functional/tests.py +++ b/tests/functional/tests.py @@ -2,7 +2,7 @@ import fnmatch import os from subprocess import call -from raven.utils.compat import TestCase +from raven.utils.testutils import BaseTestCase as TestCase ROOT = os.path.normpath( diff --git a/tests/processors/tests.py b/tests/processors/tests.py index ae48c62..6271bdd 100644 --- a/tests/processors/tests.py +++ b/tests/processors/tests.py @@ -62,6 +62,13 @@ def get_http_data(): return data +def get_extra_data(): + data = get_stack_trace_data_real() + + data['extra'] = VARS + return data + + class SanitizePasswordsProcessorTest(TestCase): def _check_vars_sanitized(self, vars, proc): @@ -118,6 +125,17 @@ class SanitizePasswordsProcessorTest(TestCase): self.assertTrue(n in http) self._check_vars_sanitized(http[n], proc) + def test_extra(self): + data = get_extra_data() + + proc = SanitizePasswordsProcessor(Mock()) + result = proc.process(data) + + self.assertTrue('extra' in result) + extra = result['extra'] + + self._check_vars_sanitized(extra, proc) + def test_querystring_as_string(self): data = get_http_data() data['request']['query_string'] = 'foo=bar&password=hello&the_secret=hello'\ @@ -7,4 +7,6 @@ envlist = py26, py27, py30, py31, py32, py33, pypy [testenv] -commands = python setup.py test +commands = + pip install -e .[tests] + python setup.py test |
