pythonsentryturbogears2turbogearsraven

TurboGears and backlash: How to add extra, per-request context to Raven for more informative Sentry reports?


Through backlash, TurboGears supports error reporting to Sentry via Raven. Enabling the error reporting is quite easy, just add the appropriate setting in the .ini configuration file, for example:

[DEFAULT]
debug = false
trace_errors.sentry_dsn = https://[…]
trace_slowreqs.enable = true
trace_slowreqs.sentry_dsn = https://[…]

set debug = false

According to Raven's documentation, adding more context to what gets reported should be as simple as

def handle_request(request):  # In TurboGears, this would be a controller instead.
    client.context.merge({'user': {
        'email': request.user.email
    }})
    try:
        ...
    finally:
        client.context.clear()

However, now I wonder what is the easiest, or most correct, way to get hold of the client instance that backlash will use for reporting? I would like to add per-request information, typically from within the request handlers, or Controller methods.


Solution

  • Editing the raven context is currently quite hard as the error reporters are not registered anywhere, so you cannot say "hey give me the error reporters" and look for the Sentry one in that list.

    Currently the only way is to register an after_config hook, gather the Raven Client during the configuration process and store it somewhere accessible.

    Changing backlash middlewares to store the reporters somewhere accessible should be fairly easy (e.g. the environ) but currently it's not available.

    By the way here is a short example of the after_config solution that should make the client available as tg.app_globals.sentry_clients, copy it in your app_cfg.py and it should do what you expect (didn't have time to try it, sorry if you find errors), then you can get the context from the client whenever is needed:

    def gather_sentry_client(app):
        from backlash import TraceErrorsMiddleware, TraceSlowRequestsMiddleware
    
        try:
            trace_errors_app = app.app.application
        except:
            return app
    
        if not isinstance(trace_errors_app, TraceErrorsMiddleware):
            return app
    
        trace_errors_client = None
        for reporter in trace_errors_app.reporters:
            if hasattr(reporter, 'client'):
                trace_errors_client = reporter.client
    
        slow_reqs_app = trace_errors_app.app
        slow_reqs_client = None
        if isinstance(slow_reqs_app, TraceSlowRequestsMiddleware):
            for reporter in slow_reqs_app.reporters:
                if hasattr(reporter, 'client'):
                    slow_reqs_client = reporter.client
    
        from tg import config
        app_globals = config['tg.app_globals']
        app_globals.sentry_clients = {
            'errors': trace_errors_client,
            'slowreqs': slow_reqs_client
        }
        return app    
    
    from tg import hooks
    hooks.register('after_config', gather_sentry_client)