2

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.

Martin Thorsen Ranang
  • 2,394
  • 1
  • 28
  • 43

1 Answers1

4

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)
amol
  • 1,771
  • 1
  • 11
  • 15
  • Thanks for your suggestion, Alessandro. But, since the usage pattern would typically be to set some per-request contextual info, wouldn't sharing _the same_ `raven.base.Client` instance globally, in `tg.app_globals.sentry_clients`, possibly cause race conditions? I suspect that `backlash` might need a rewrite to support late, request-local initialization of the reporter. – Martin Thorsen Ranang Nov 15 '15 at 07:44
  • I see now, from the docstring in `raven.base.Client.context()` that it "Updates this clients thread-local context for future events." But, I see no code in the `Context` implementation that makes it seem _thread-local_. – Martin Thorsen Ranang Nov 15 '15 at 10:49
  • Context inherits from threading.local -> https://github.com/getsentry/raven-python/blob/master/raven/context.py#L16 so any attribute you assign to it is actually thread local, including ``data`` where all the context data is stored. – amol Nov 15 '15 at 11:17
  • *blush* Thanks. I didn't notice. – Martin Thorsen Ranang Nov 15 '15 at 11:19
  • I can confirm that this works like a charm. Thank you! My test usage was to place `app_globals.sentry_clients['errors'].user_context({'email': 'foo@example.com'})` inside the `RootController._before()` method. – Martin Thorsen Ranang Nov 15 '15 at 11:50