From 27cc40e6d5669e2275b89541711dea8bb6f6e309 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 27 Jan 2016 15:03:21 -0800 Subject: [PATCH] Remember exceptions that are logged and filter out duplicates. This fixes #724 --- CHANGES | 7 +++++++ raven/base.py | 22 ++++++++++++++++++++++ raven/context.py | 2 ++ tests/contrib/flask/tests.py | 7 +++++++ 4 files changed, 38 insertions(+) diff --git a/CHANGES b/CHANGES index bb710940..0945e170 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +Version 5.10.2 +-------------- + +* Remember exceptions in flight until the context is cleared so that two + reports with the same exception data do not result in two errors + being logged. + Version 5.10.1 -------------- diff --git a/raven/base.py b/raven/base.py index d0995704..549073c9 100644 --- a/raven/base.py +++ b/raven/base.py @@ -282,6 +282,20 @@ class Client(object): return url return '%s:%s' % (scheme, url) + def _get_exception_key(self, exc_info): + return ( + id(exc_info[1]), + id(exc_info[2]), + ) + + def skip_error_for_logging(self, exc_info): + key = self._get_exception_key(exc_info) + return key in self.context.exceptions_to_skip + + def record_exception_seen(self, exc_info): + key = self._get_exception_key(exc_info) + self.context.exceptions_to_skip.add(key) + def build_msg(self, event_type, data=None, date=None, time_spent=None, extra=None, stack=None, public_key=None, tags=None, fingerprint=None, **kwargs): @@ -530,6 +544,12 @@ class Client(object): if not self.is_enabled(): return + exc_info = kwargs.get('exc_info') + if exc_info is not None: + if self.skip_error_for_logging(exc_info): + return + self.record_exception_seen(exc_info) + data = self.build_msg( event_type, data, date, time_spent, extra, stack, tags=tags, **kwargs) @@ -701,6 +721,8 @@ class Client(object): ``kwargs`` are passed through to ``.capture``. """ + if exc_info is None: + exc_info = sys.exc_info() return self.capture( 'raven.events.Exception', exc_info=exc_info, **kwargs) diff --git a/raven/context.py b/raven/context.py index e941360c..b1c3d7b5 100644 --- a/raven/context.py +++ b/raven/context.py @@ -27,6 +27,7 @@ class Context(local, Mapping, Iterable): """ def __init__(self): self.data = {} + self.exceptions_to_skip = set() def __getitem__(self, key): return self.data[key] @@ -58,3 +59,4 @@ class Context(local, Mapping, Iterable): def clear(self): self.data = {} + self.exceptions_to_skip.clear() diff --git a/tests/contrib/flask/tests.py b/tests/contrib/flask/tests.py index 072cec80..90f5a58e 100644 --- a/tests/contrib/flask/tests.py +++ b/tests/contrib/flask/tests.py @@ -9,6 +9,7 @@ from flask.ext.login import LoginManager, AnonymousUserMixin, login_user from raven.base import Client from raven.contrib.flask import Sentry from raven.utils.testutils import TestCase +from raven.handlers.logging import SentryHandler class TempStoreClient(Client): @@ -125,6 +126,12 @@ class FlaskTest(BaseTest): self.assertEquals(event['message'], 'ValueError: hello world') self.assertEquals(event['culprit'], 'tests.contrib.flask.tests in an_error') + def test_capture_plus_logging(self): + client, raven, app = self.make_client_and_raven(debug=False) + app.logger.addHandler(SentryHandler(raven)) + client.get('/an-error/') + assert len(raven.events) == 1 + def test_get(self): response = self.client.get('/an-error/?foo=bar') self.assertEquals(response.status_code, 500)