Skip to content
This repository was archived by the owner on Oct 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
Version 5.4.0
-------------

* Binding transports via a scheme prefix on DSNs is now deprecated.
* ``raven.conf.load`` has been removed.
* Upstream-related configuration (such as url, project_id, and keys) is now contained in ``RemoteConfig``
attached to ``Client.remote``

Version 5.3.1
-------------

Expand Down
5 changes: 0 additions & 5 deletions docs/config/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,6 @@ It is composed of six important pieces:

* The project ID which the authenticated user is bound to.

.. note::

Protocol may also contain transporter type: gevent+http, gevent+https, twisted+http, tornado+http, eventlet+http, eventlet+https

For *Python 3.3+* also available: aiohttp+http and aiohttp+https

Client Arguments
----------------
Expand Down
89 changes: 25 additions & 64 deletions docs/transports/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ Transports

A transport is the mechanism in which Raven sends the HTTP request to the Sentry server. By default, Raven uses a threaded asynchronous transport, but you can easily adjust this by modifying your ``SENTRY_DSN`` value.

Transport registration is done via the URL prefix, so for example, a synchronous transport is as simple as prefixing your ``SENTRY_DSN`` with the ``sync+`` value.
Transport registration is done as part of the Client configuration:

.. code-block:: python

# Use the synchronous HTTP transport
client = Client('http://public:secret@example.com/1', transport=HTTPTransport)

Options are passed to transports via the querystring.

Expand All @@ -25,82 +30,38 @@ For example, to increase the timeout and to disable SSL verification:
SENTRY_DSN = 'http://public:secret@example.com/1?timeout=5&verify_ssl=0'


aiohttp
-------

Should only be used within a :pep:`3156` compatible event loops
(*asyncio* itself and others).

::

SENTRY_DSN = 'aiohttp+http://public:secret@example.com/1'

Eventlet
--------

Should only be used within an Eventlet IO loop.

::

SENTRY_DSN = 'eventlet+http://public:secret@example.com/1'


Gevent
------

Should only be used within a Gevent IO loop.

::

SENTRY_DSN = 'gevent+http://public:secret@example.com/1'


Requests
--------

Requires the ``requests`` library. Synchronous.

::

SENTRY_DSN = 'requests+http://public:secret@example.com/1'


Sync
----

A synchronous blocking transport.

::

SENTRY_DSN = 'sync+http://public:secret@example.com/1'
Builtin Transports
------------------

.. data:: sentry.transport.thread.ThreadedHTTPTransport

Threaded (Default)
------------------
The default transport. Manages a threaded worker for processing messages asynchronous.

Spawns an async worker for processing messages.
.. data:: sentry.transport.http.HTTPTransport

::
A synchronous blocking transport.

SENTRY_DSN = 'threaded+http://public:secret@example.com/1'
.. data:: sentry.transport.aiohttp.AioHttpTransport

Should only be used within a :pep:`3156` compatible event loops
(*asyncio* itself and others).

Tornado
-------
.. data:: sentry.transport.eventlet.EventletHTTPTransport

Should only be used within a Tornado IO loop.
Should only be used within an Eventlet IO loop.

::
.. data:: sentry.transport.gevent.GeventedHTTPTransport

SENTRY_DSN = 'tornado+http://public:secret@example.com/1'
Should only be used within a Gevent IO loop.

.. data:: sentry.transport.requests.RequestsHTTPTransport

Twisted
-------
A synchronous transport which relies on the ``requests`` library.

Should only be used within a Twisted event loop.
.. data:: sentry.transport.tornado.TornadoHTTPTransport

::
Should only be used within a Tornado IO loop.

SENTRY_DSN = 'twisted+http://public:secret@example.com/1'
.. data:: sentry.transport.twisted.TwistedHTTPTransport

Should only be used within a Twisted event loop.
4 changes: 2 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ the optional DSN argument::

You should get something like the following, assuming you're configured everything correctly::

$ raven test sync+http://dd2c825ff9b1417d88a99573903ebf80:91631495b10b45f8a1cdbc492088da6a@localhost:9000/1
$ raven test http://dd2c825ff9b1417d88a99573903ebf80:91631495b10b45f8a1cdbc492088da6a@localhost:9000/1
Using DSN configuration:
sync+http://dd2c825ff9b1417d88a99573903ebf80:91631495b10b45f8a1cdbc492088da6a@localhost:9000/1
http://dd2c825ff9b1417d88a99573903ebf80:91631495b10b45f8a1cdbc492088da6a@localhost:9000/1

Client configuration:
servers : ['http://localhost:9000/api/store/']
Expand Down
2 changes: 1 addition & 1 deletion raven/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from raven.versioning import * # NOQA


__all__ = ('VERSION', 'Client', 'load', 'get_version')
__all__ = ('VERSION', 'Client', 'get_version')

try:
VERSION = __import__('pkg_resources') \
Expand Down
95 changes: 37 additions & 58 deletions raven/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@

import raven
from raven.conf import defaults
from raven.conf.remote import RemoteConfig
from raven.context import Context
from raven.exceptions import APIError, RateLimited
from raven.utils import six, json, get_versions, get_auth_header, merge_dicts
from raven.utils.encoding import to_unicode
from raven.utils.serializer import transform
from raven.utils.stacks import get_stack_info, iter_stack_frames, get_culprit
from raven.utils.urlparse import urlparse
from raven.transport.registry import TransportRegistry, default_transports

__all__ = ('Client',)
Expand Down Expand Up @@ -117,7 +117,7 @@ class Client(object):

_registry = TransportRegistry(transports=default_transports)

def __init__(self, dsn=None, raise_send_errors=False, **options):
def __init__(self, dsn=None, raise_send_errors=False, transport=None, **options):
global Raven

o = options
Expand All @@ -134,8 +134,8 @@ def __init__(self, dsn=None, raise_send_errors=False, **options):
self.error_logger = logging.getLogger('sentry.errors')
self.uncaught_logger = logging.getLogger('sentry.errors.uncaught')

self.dsns = {}
self.set_dsn(dsn, **options)
self._transport_cache = {}
self.set_dsn(dsn, transport)

self.include_paths = set(o.get('include_paths') or [])
self.exclude_paths = set(o.get('exclude_paths') or [])
Expand Down Expand Up @@ -174,42 +174,27 @@ def __init__(self, dsn=None, raise_send_errors=False, **options):

self._context = Context()

def set_dsn(self, dsn=None, **options):
o = options

def set_dsn(self, dsn=None, transport=None):
if dsn is None and os.environ.get('SENTRY_DSN'):
msg = "Configuring Raven from environment variable 'SENTRY_DSN'"
self.logger.debug(msg)
dsn = os.environ['SENTRY_DSN']

try:
servers, public_key, secret_key, project, transport_options = self.dsns[dsn]
except KeyError:
if dsn:
# TODO: should we validate other options weren't sent?
urlparts = urlparse(dsn)
self.logger.debug(
"Configuring Raven for host: %s://%s:%s" % (urlparts.scheme,
urlparts.netloc, urlparts.path))
dsn_config = raven.load(dsn, transport_registry=self._registry)
servers = dsn_config['SENTRY_SERVERS']
project = dsn_config['SENTRY_PROJECT']
public_key = dsn_config['SENTRY_PUBLIC_KEY']
secret_key = dsn_config['SENTRY_SECRET_KEY']
transport_options = dsn_config.get('SENTRY_TRANSPORT_OPTIONS', {})
if dsn not in self._transport_cache:
if dsn is None:
result = RemoteConfig(transport=transport)
else:
servers = ()
project = None
public_key = None
secret_key = None
transport_options = {}
self.dsns[dsn] = servers, public_key, secret_key, project, transport_options

self.servers = servers
self.public_key = public_key
self.secret_key = secret_key
self.project = project or defaults.PROJECT
self.transport_options = transport_options
result = RemoteConfig.from_string(
dsn,
transport=transport,
transport_registry=self._registry,
)
self._transport_cache[dsn] = result
self.remote = result
else:
self.remote = self._transport_cache[dsn]

self.logger.debug("Configuring Raven for host: {0}".format(self.remote))

@classmethod
def register_scheme(cls, scheme, transport_class):
Expand Down Expand Up @@ -252,14 +237,6 @@ def get_ident(self, result):
def get_handler(self, name):
return self.module_cache[name](self)

def _get_public_dsn(self):
url = urlparse(self.servers[0])
netloc = url.hostname
if url.port:
netloc += ':%s' % url.port
path = url.path.replace('api/%s/store/' % (self.project,), self.project)
return '//%s@%s%s' % (self.public_key, netloc, path)

def get_public_dsn(self, scheme=None):
"""
Returns a public DSN which is consumable by raven-js
Expand All @@ -272,7 +249,7 @@ def get_public_dsn(self, scheme=None):
"""
if not self.is_enabled():
return
url = self._get_public_dsn()
url = self.remote.get_public_dsn()
if not scheme:
return url
return '%s:%s' % (scheme, url)
Expand Down Expand Up @@ -397,7 +374,7 @@ def build_msg(self, event_type, data=None, date=None,
data['extra'][k] = self.transform(v)

# It's important date is added **after** we serialize
data.setdefault('project', self.project)
data.setdefault('project', self.remote.project)
data.setdefault('timestamp', date or datetime.utcnow())
data.setdefault('time_spent', time_spent)
data.setdefault('event_id', event_id)
Expand Down Expand Up @@ -532,7 +509,7 @@ def is_enabled(self):
Return a boolean describing whether the client should attempt to send
events.
"""
return bool(self.servers)
return self.remote.is_active()

def _successful_send(self):
self.state.set_success()
Expand Down Expand Up @@ -590,9 +567,7 @@ def failed_send(e):
self._failed_send(e, url, self.decode(data))

try:
parsed = urlparse(url)
transport = self._registry.get_transport(
parsed, **self.transport_options)
transport = self.remote.get_transport()
if transport.async:
transport.async_send(data, headers, self._successful_send,
failed_send)
Expand Down Expand Up @@ -626,18 +601,22 @@ def send_encoded(self, message, auth_header=None, **kwargs):
protocol=self.protocol_version,
timestamp=timestamp,
client=client_string,
api_key=self.public_key,
api_secret=self.secret_key,
api_key=self.remote.public_key,
api_secret=self.remote.secret_key,
)

for url in self.servers:
headers = {
'User-Agent': client_string,
'X-Sentry-Auth': auth_header,
'Content-Type': 'application/octet-stream',
}

self.send_remote(url=url, data=message, headers=headers)
headers = {
'User-Agent': client_string,
'X-Sentry-Auth': auth_header,
'Content-Type': 'application/octet-stream',
}

self.send_remote(
url=self.remote.store_endpoint,
data=message,
headers=headers,
**kwargs
)

def encode(self, data):
"""
Expand Down
39 changes: 1 addition & 38 deletions raven/conf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
from __future__ import absolute_import

import logging
from raven.utils.urlparse import urlparse

__all__ = ('load', 'setup_logging')
__all__ = ['setup_logging']

EXCLUDE_LOGGER_DEFAULTS = (
'raven',
Expand All @@ -21,42 +20,6 @@
)


# TODO (vng): this seems weirdly located in raven.conf. Seems like
# it's really a part of raven.transport.TransportRegistry
# Not quite sure what to do with this
def load(dsn, scope=None, transport_registry=None):
"""
Parses a Sentry compatible DSN and loads it
into the given scope.

>>> import raven

>>> dsn = 'https://public_key:secret_key@sentry.local/project_id'

>>> # Apply configuration to local scope
>>> raven.load(dsn, locals())

>>> # Return DSN configuration
>>> options = raven.load(dsn)
"""

if not transport_registry:
from raven.transport import TransportRegistry, default_transports
transport_registry = TransportRegistry(default_transports)

url = urlparse(dsn)

if not transport_registry.supported_scheme(url.scheme):
raise ValueError('Unsupported Sentry DSN scheme: %r' % url.scheme)

if scope is None:
scope = {}
scope_extras = transport_registry.compute_scope(url, scope)
scope.update(scope_extras)

return scope


def setup_logging(handler, exclude=EXCLUDE_LOGGER_DEFAULTS):
"""
Configures logging to pipe to Sentry.
Expand Down
Loading