Skip to content

Commit

Permalink
Log SystemExit (with non-zero exit code) and KeyboardInterrupt in WSG…
Browse files Browse the repository at this point in the history
…I middleware

Neither SystemExit nor KeyboardInterrupt subclass Exception, so we must
explicitly handle these two exception classes in addition to Exception in the
WSGI middleware.

This is notably a direct port from raven-python:
 - https://github.com/getsentry/raven-python/blob/master/raven/middleware.py
 - https://github.com/getsentry/raven-python/blob/master/tests/middleware/tests.py

Fixes: getsentryGH-379
  • Loading branch information
saifelse committed May 30, 2019
1 parent b959843 commit f8213b8
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 2 deletions.
17 changes: 15 additions & 2 deletions sentry_sdk/integrations/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ def __call__(self, environ, start_response):
rv = self.app(environ, start_response)
except Exception:
reraise(*_capture_exception(hub))
except KeyboardInterrupt:
reraise(*_capture_exception(self._hub))
except SystemExit as e:
reraise(*_capture_exception(self._hub, skip_capture=e.code == 0))

return _ScopedResponse(hub, rv)

Expand Down Expand Up @@ -149,7 +153,7 @@ def get_client_ip(environ):
return environ.get("REMOTE_ADDR")


def _capture_exception(hub):
def _capture_exception(hub, skip_capture=False):
# type: (Hub) -> ExcInfo
# Check client here as it might have been unset while streaming response
if hub.client is not None:
Expand All @@ -159,7 +163,8 @@ def _capture_exception(hub):
client_options=hub.client.options,
mechanism={"type": "wsgi", "handled": False},
)
hub.capture_event(event, hint=hint)
if not skip_capture:
hub.capture_event(event, hint=hint)
return exc_info


Expand All @@ -183,6 +188,10 @@ def __iter__(self):
break
except Exception:
reraise(*_capture_exception(self._hub))
except KeyboardInterrupt:
reraise(*_capture_exception(self._hub))
except SystemExit as e:
reraise(*_capture_exception(self._hub, skip_capture=e.code == 0))

yield chunk

Expand All @@ -194,6 +203,10 @@ def close(self):
pass
except Exception:
reraise(*_capture_exception(self._hub))
except KeyboardInterrupt:
reraise(*_capture_exception(self._hub))
except SystemExit as e:
reraise(*_capture_exception(self._hub, skip_capture=e.code == 0))


def _make_wsgi_event_processor(environ):
Expand Down
75 changes: 75 additions & 0 deletions tests/integrations/wsgi/test_wsgi.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import logging

from werkzeug.test import Client
import pytest

Expand All @@ -12,6 +14,28 @@ def app(environ, start_response):
return app


class IterableApp(object):
def __init__(self, iterable):
self.iterable = iterable

def __call__(self, environ, start_response):
return self.iterable


class ExitingIterable(object):
def __init__(self, exc_func):
self._exc_func = exc_func

def __iter__(self):
return self

def __next__(self):
raise self._exc_func()

def next(self):
return type(self).__next__(self)


def test_basic(sentry_init, crashing_app, capture_events):
sentry_init(send_default_pii=True)
app = SentryWsgiMiddleware(crashing_app)
Expand All @@ -30,3 +54,54 @@ def test_basic(sentry_init, crashing_app, capture_events):
"query_string": "",
"url": "http://localhost/",
}


def test_systemexit_0_is_ignored(sentry_init, capture_events):
sentry_init(send_default_pii=True)
iterable = ExitingIterable(lambda: SystemExit(0))
app = SentryWsgiMiddleware(IterableApp(iterable))
client = Client(app)
events = capture_events()

with pytest.raises(SystemExit):
client.get("/")

assert len(events) == 0


def test_systemexit_1_is_captured(sentry_init, capture_events):
sentry_init(send_default_pii=True)
iterable = ExitingIterable(lambda: SystemExit(1))
app = SentryWsgiMiddleware(IterableApp(iterable))
client = Client(app)
events = capture_events()

with pytest.raises(SystemExit):
client.get("/")

event, = events

assert "exception" in event
exc = event["exception"]["values"][-1]
assert exc["type"] == "SystemExit"
assert exc["value"] == "1"
assert event["level"] == "error"


def test_keyboard_interrupt_is_captured(sentry_init, capture_events):
sentry_init(send_default_pii=True)
iterable = ExitingIterable(lambda: KeyboardInterrupt())
app = SentryWsgiMiddleware(IterableApp(iterable))
client = Client(app)
events = capture_events()

with pytest.raises(KeyboardInterrupt):
client.get("/")

event, = events

assert "exception" in event
exc = event["exception"]["values"][-1]
assert exc["type"] == "KeyboardInterrupt"
assert exc["value"] == ""
assert event["level"] == "error"

0 comments on commit f8213b8

Please sign in to comment.