Skip to content
Merged
24 changes: 24 additions & 0 deletions sentry_sdk/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,27 @@ def __new__(cls, name, this_bases, d):
return meta(name, bases, d)

return type.__new__(metaclass, "temporary_class", (), {})


def check_thread_support():
try:
from uwsgi import opt
except ImportError:
return

# When `threads` is passed in as a uwsgi option,
# `enable-threads` is implied on.
if "threads" in opt:
return

if str(opt.get("enable-threads", "0")).lower() in ("false", "off", "no", "0"):
from warnings import warn

warn(
Warning(
"We detected the use of uwsgi with disabled threads. "
"This will cause issues with the transport you are "
"trying to use. Please enable threading for uwsgi. "
'(Enable the "enable-threads" flag).'
)
)
38 changes: 21 additions & 17 deletions sentry_sdk/client.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import os
import uuid
import random
import atexit
from datetime import datetime

from ._compat import string_types
Expand All @@ -11,6 +10,7 @@
convert_types,
handle_in_app,
get_type_name,
logger,
)
from .transport import make_transport
from .consts import DEFAULT_OPTIONS, SDK_INFO
Expand Down Expand Up @@ -42,7 +42,7 @@ def get_options(*args, **kwargs):
class Client(object):
def __init__(self, *args, **kwargs):
self.options = options = get_options(*args, **kwargs)
self._transport = make_transport(options)
self.transport = make_transport(options)

request_bodies = ("always", "never", "small", "medium")
if options["request_bodies"] not in request_bodies:
Expand All @@ -52,9 +52,6 @@ def __init__(self, *args, **kwargs):
)
)

# XXX: we should probably only do this for the init()ed client
atexit.register(self.close)

@property
def dsn(self):
"""Returns the configured dsn."""
Expand Down Expand Up @@ -84,7 +81,10 @@ def _prepare_event(self, event, hint, scope):

before_send = self.options["before_send"]
if before_send is not None:
event = before_send(event)
new_event = before_send(event)
if new_event is None:
logger.info("before send dropped event (%s)", event)
event = new_event

# Postprocess the event in the very end so that annotated types do
# generally not surface in before_send
Expand Down Expand Up @@ -129,24 +129,28 @@ def _should_capture(self, event, hint=None, scope=None):

def capture_event(self, event, hint=None, scope=None):
"""Captures an event."""
if self._transport is None:
if self.transport is None:
return
rv = event.get("event_id")
if rv is None:
event["event_id"] = rv = uuid.uuid4().hex
if self._should_capture(event, hint, scope):
event = self._prepare_event(event, hint, scope)
if event is not None:
self._transport.capture_event(event)
self.transport.capture_event(event)
return rv

def drain_events(self, timeout=None):
if timeout is None:
timeout = self.options["shutdown_timeout"]
if self._transport is not None:
self._transport.drain_events(timeout)
def close(self, timeout=None, shutdown_callback=None):
"""Closes the client which shuts down the transport in an
orderly manner.
"""
if self.transport is not None:
if timeout is None:
timeout = self.options["shutdown_timeout"]
self.transport.shutdown(timeout=timeout, callback=shutdown_callback)

def __enter__(self):
return self

def close(self):
self.drain_events()
if self._transport is not None:
self._transport.close()
def __exit__(self, exc_type, exc_value, tb):
self.close()
1 change: 1 addition & 0 deletions sentry_sdk/consts.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import socket


VERSION = "0.1"
DEFAULT_SERVER_NAME = socket.gethostname() if hasattr(socket, "gethostname") else None
DEFAULT_OPTIONS = {
Expand Down
4 changes: 4 additions & 0 deletions sentry_sdk/hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ def add_breadcrumb(self, *args, **kwargs):
"""Adds a breadcrumb."""
client, scope = self._stack[-1]
if client is None:
logger.info("Dropped breadcrumb because no client bound")
return

if not kwargs and len(args) == 1 and callable(args[0]):
Expand All @@ -172,11 +173,14 @@ def add_breadcrumb(self, *args, **kwargs):
if crumb.get("type") is None:
crumb["type"] = "default"

original_crumb = crumb
if client.options["before_breadcrumb"] is not None:
crumb = client.options["before_breadcrumb"](crumb)

if crumb is not None:
scope._breadcrumbs.append(crumb)
else:
logger.info("before breadcrumb dropped breadcrumb (%s)", original_crumb)
while len(scope._breadcrumbs) >= client.options["max_breadcrumbs"]:
scope._breadcrumbs.popleft()

Expand Down
2 changes: 2 additions & 0 deletions sentry_sdk/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ def _get_default_integrations():
from .logging import LoggingIntegration
from .excepthook import ExcepthookIntegration
from .dedupe import DedupeIntegration
from .atexit import AtexitIntegration

yield LoggingIntegration
yield ExcepthookIntegration
yield DedupeIntegration
yield AtexitIntegration


def setup_integrations(options):
Expand Down
32 changes: 32 additions & 0 deletions sentry_sdk/integrations/atexit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from __future__ import absolute_import

import os
import atexit

from sentry_sdk.hub import Hub
from sentry_sdk.utils import logger
from . import Integration


def default_shutdown_callback(pending, timeout):
print("Sentry is attempting to send %i pending error messages" % pending)
print("Waiting up to %s seconds" % timeout)
print("Press Ctrl-%s to quit" % (os.name == "nt" and "Break" or "C"))


class AtexitIntegration(Integration):
identifier = "atexit"

def __init__(self, callback=None):
if callback is None:
callback = default_shutdown_callback
self.callback = callback

def install(self):
@atexit.register
def _shutdown():
main_client = Hub.main.client
logger.debug("atexit: got shutdown signal")
if main_client is not None:
logger.debug("atexit: shutting down client")
main_client.close(shutdown_callback=self.callback)
20 changes: 14 additions & 6 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from .utils import logger


class Scope(object):
__slots__ = ["_data", "_breadcrumbs", "_event_processors", "_error_processors"]

Expand Down Expand Up @@ -73,6 +76,9 @@ def func(event, exc_info):
self._error_processors.append(func)

def apply_to_event(self, event, hint=None):
def _drop(event, cause, ty):
logger.info("%s (%s) dropped event (%s)", ty, cause, event)

event.setdefault("breadcrumbs", []).extend(self._breadcrumbs)
if event.get("user") is None and "user" in self._data:
event["user"] = self._data["user"]
Expand All @@ -99,14 +105,16 @@ def apply_to_event(self, event, hint=None):
if hint is not None and hint.exc_info is not None:
exc_info = hint.exc_info
for processor in self._error_processors:
event = processor(event, exc_info)
if event is None:
return
new_event = processor(event, exc_info)
if new_event is None:
return _drop(event, processor, "error processor")
event = new_event

for processor in self._event_processors:
event = processor(event)
if event is None:
return None
new_event = processor(event)
if new_event is None:
return _drop(event, processor, "event processor")
event = new_event

return event

Expand Down
Loading