summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Ordoquy <xordoquy@linovia.com>2015-03-01 13:15:59 +0100
committerXavier Ordoquy <xordoquy@linovia.com>2015-03-01 13:15:59 +0100
commitb58cd382366c5487cf6fb8ec956006eff4a9a87a (patch)
tree63d03f99ee69ae5b54fa7fa74b6640da1d9e9a64
parent49e5ee859bf1c126f8353a3d75d50fbe08463f89 (diff)
parentc29dbde03b4ee9df47169210da00333d7290d262 (diff)
downloadraven-feature/django_removal.tar.gz
Merge remote-tracking branch 'origin/master' into feature/django_removalfeature/django_removal
-rw-r--r--.gitignore1
-rw-r--r--.travis.yml8
-rw-r--r--CHANGES2
-rw-r--r--MANIFEST.in1
-rw-r--r--conftest.py1
-rw-r--r--docs/config/index.rst4
-rw-r--r--docs/integrations/celery.rst3
-rw-r--r--docs/integrations/django.rst12
-rw-r--r--raven/base.py34
-rw-r--r--raven/contrib/celery/__init__.py4
-rw-r--r--raven/contrib/django/management/__init__.py4
-rw-r--r--raven/contrib/django/models.py7
-rw-r--r--raven/contrib/zope/__init__.py8
-rw-r--r--raven/handlers/logging.py4
-rw-r--r--raven/processors.py9
-rw-r--r--raven/utils/compat.py5
-rw-r--r--raven/utils/testutils.py5
-rw-r--r--raven/versioning.py8
-rwxr-xr-xsetup.py8
-rw-r--r--tests/functional/tests.py2
-rw-r--r--tests/processors/tests.py18
-rw-r--r--tox.ini4
22 files changed, 110 insertions, 42 deletions
diff --git a/.gitignore b/.gitignore
index 941b348..d1ff3d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/CHANGES b/CHANGES
index 9c675a2..f3d6722 100644
--- a/CHANGES
+++ b/CHANGES
@@ -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
diff --git a/setup.py b/setup.py
index 19e9a25..0512d6b 100755
--- a/setup.py
+++ b/setup.py
@@ -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'\
diff --git a/tox.ini b/tox.ini
index df54ed9..f0fa067 100644
--- a/tox.ini
+++ b/tox.ini
@@ -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