Switching to Sentry-Python

If you want to move to the new sentry-python SDK we provided a short guide here of the most common patterns:

Migration

Here are some examples of how the new SDKs work. Please note that the API for all Python SDKs is the same.

Installation

Old :

import raven
client = raven.Client('___PUBLIC_DSN___', release="1.3.0")

New :

import sentry_sdk
sentry_sdk.init('___PUBLIC_DSN___', release='1.3.0')

Set a global tag

Old :

client.tags_context({'key': 'value'})

New :

with sentry_sdk.configure_scope() as scope:
    scope.set_tag('key', 'value')

Capture custom exception

Old :

try:
    throwing_function()
except Exception:
    client.captureException(extra={'debug': False})

New :

try:
    throwing_function()
except Exception as e:
    with sentry_sdk.push_scope() as scope:
        scope.set_extra('debug', False)
        sentry_sdk.capture_exception(e)

Capture a message

Old :

client.captureMessage('test', level='info', extra={'debug': False})

New :

with sentry_sdk.push_scope() as scope:
    scope.set_extra('debug', False)
    sentry_sdk.capture_message('test', 'info')

Breadcrumbs

Old :

from raven import breadcrumbs import

breadcrumbs.record(
    message='Item added to shopping cart',
    category='action',
    data={
        'isbn': '978-1617290541',
        'cartSize': '3'
    }
)

New :

sentry_sdk.add_breadcrumb(
  message='Item added to shopping cart',
  category='action',
  data={
    'isbn': '978-1617290541',
    'cartSize': '3'
  }
)
2 Likes

Hi,

Is there any way to express old raven processors setup in new sentry_sdk?
What I’m mostly interested in is the raven.processors.SanitizePasswordsProcessor.

Thanks

1 Like

Hi @byo
We don’t have a built-in password processor (mostly because it was never working perfectly for all usecases), but you can modify your events using before_send: https://docs.sentry.io/learn/filtering/?platform=python

Hi, Is it still possible to configure Sentry using dictConfig?

Hi, please open a new thread

Hi there, is there a recommended way to migrate get_sentry_user_info from the old raven Tornado integration?

https://www.pydoc.io/pypi/raven-5.27.1/autoapi/tornado/index.html#tornado.SentryMixin.get_sentry_user_info

I realize that the User interface is now handled through scopes, but is there a hook to set the scope when the exception is being handled?

If you enable https://docs.sentry.io/platforms/python/tornado/ this should all happen automatically. If not, it’s a bug.

Or did you overwrite this func to add more user info?

Hi Markus, we did indeed overwrite get_sentry_user_info to include the user id, name and email. The new Tornado implementation only provides “is_authenticated”.

It also appears the cookies are being sent incorrectly:

request.cookies.0.1: Discarded invalid value
Reason: expected a string
Value:
{
comment: , 
domain: , 
expires: , 
httponly: , 
max-age: , 
path: , 
secure: , 
version: 
}

Overall the new Tornado integration looks great, btw, much easier to implement!

Cheers,

-Nick

Thanks, I will fix the bug about cookie data.

Is your implementation of get_sentry_user_info so costly as to not run it on every request? We do have a way to register callbacks on scopes (Scope.add_event_processor), it just needs nicer documentation. Basically something like this:

with configure_scope() as scope:
    @scope.add_event_processor
    def _(event, hint):
        user = event.setdefault("user", {})
        user["username"] = "foo"
        return event

That code will only run when an event is sent, and the callback is put on the current scope (we push a scope when a request starts and pop a scope when a request is finished)

btw thank you very much for the casual link to pydoc.io, this seems very useful for our own purposes :slight_smile:

Thanks so much for the fast replies!

The issue isn’t so much of having to run get_sentry_user_info on every request, but more about only doing so once the RequestHandler has identified the current user.

Tornado handles authentication through a get_current_user function that the app developer overrides. Without the hook, we’d have to edit every handler that implements get_current_user to also set the sentry user context at that time.

add_event_processor sounds promising (in fact I’d also looked at before_send as an option), but the key is that I’ll need a reference to the RequestHandler since that holds the user info.

Does the event or hint hold a request to the Requesthandler? If not, where would I define the event_processor such that I could access the RequestHandler?

I understand. I assume you must have a common baseclass for all your handlers, where you previously overwrote get_sentry_user_info, is that correct? is it possible for you do one of the following:

  • to overwrite RequestHandler.prepare on that baseclass for setting the user context
  • to rename all functions get_current_user to get_current_user_impl, and have a get_current_user only on the baseclass that calls get_current_user_impl but also sets the sentry context?

That could do as a workaround, but it adds complexity and brittleness to our application code.

Given that the sentry-sdk still hasn’t reached 1.0, I don’t need to upgrade right now if there’s hope of a cleaner solution in a future release.

Is it worth discussing what that would look like? For example, could before_send receive the RequestHandler as a hint?

I assume you are talking about the second option with renaming get_current_user? I don’t really see how the first option (overwriting prepare) is less robust or verbose than overwriting get_sentry_user_info:

class MyHandlerBaseClass(RequestHandler):
    def prepare(self):
        user = self.get_user_info()
        if user is not None:
            with configure_scope():
                scope.user = {"username": user.username}

Of course that assumes you’re not setting prepare in each of your handlers explicitly. Maybe I am missing something here, I am not very familiar with how Tornado works. Thanks for bearing with me :slight_smile:

If all of this fails I personally am open to add the request handler to the hints dictionary, However, we probably need to figure out how to make the hint namespaced by integrations. This might get a bit hairy when third-party integrations (which might exist in the future, but don’t now) are involved.

Yes, keeping using Raven is definetly an option for the indefinite future. It’s basically in maintenance mode right now: Bugfixes will still be made but it certainly won’t receive new features.

We do subclass prepare in a few handlers, so we’d have to make sure those all roll up to the base handler, but that’s reasonable and likely the path I would take.

I’m certainly not pushing for the hint approach – that might not be the right way at all to do this – but just trying to hone in on the cleanest approach (given that the previous approach was to implement a single function, which was a nicely contained change). It could be that hooking into prepare is the best option.

Separate question. Raven seemed to have a concept of a “logger” tag. This was very helpful to separate Python issues from Javascript issues (much noisier), and has some support in the UI. Sentry-sdk no longer seems to send this by default. Should I just send that manually going forward, or is there an alternate approach you recommend?

We do send logger information with the new SDK whenever we have information about the logger. I am not aware of any behavioral differences there. Could you link a specific event where you expect logger, and which tag value you’d expect as well? Also the “equivalent” event by raven where this information is all there would be useful to debug this.

Sure, I’ve got both of those. Do you want me to post those here or should I message you privately? My email is associated with the storyworth Sentry account.

You can post the links directly here or PM me, either way I’ll be able to access them.

Sentry-SDK: https://sentry.io/organizations/storyworth/issues/914070151/
Raven: https://sentry.io/organizations/storyworth/issues/914051539/

Ah sorry, I remembered that this was standard behavior around the time (to set the logger to the platform/language when no other logger was found), however the new SDKs are not supposed to do that.

You can set a default logger like this:

def before_send(event, hint):
    event.setdefault("logger", "python")
    return event

Embarrassingly I can’t find any code that would set the logger to python, neither in the SDK nor in the server. But I remember that this used to behave similarly for raven-js, and that we had an explicit discussion to no longer do this because the logger field really is supposed to contain logger paths only.

Perhaps some UI updates are in order to make the platform field in the JSON payload more visible, but I think the general trend right now is to get people to split up frontend and backend into separate projects. @zeeg might have more insights here.