Improved bugsnag reporting

- Include release stage, which should be 'production' or 'development',
  and gets postfixed by '-debug' when running in debug mode.
- Properly logging remote IP address when proxied through HAProxy;
- Log user ID, email, username, roles, and capabilities;
- Remove authentication tokens from logged session;
- Log request data and JSON separately.
- Added request endpoint.
This commit is contained in:
Sybren A. Stüvel 2017-09-28 13:28:12 +02:00
parent ec42d033b3
commit 6a541e0662
3 changed files with 59 additions and 2 deletions

View File

@ -183,9 +183,14 @@ class PillarServer(Eve):
import bugsnag
from bugsnag.handlers import BugsnagHandler
release_stage = self.config.get('BUGSNAG_RELEASE_STAGE', 'unconfigured')
if self.config.get('DEBUG'):
release_stage += '-debug'
bugsnag.configure(
api_key=bugsnag_api_key,
project_root="/data/git/pillar/pillar",
release_stage=release_stage
)
bs_handler = BugsnagHandler()
@ -196,9 +201,9 @@ class PillarServer(Eve):
# but it passes the app to the connect() call, which causes an
# error. Since we only have one app, we can do without.
from flask import got_request_exception
from bugsnag.flask import add_flask_request_to_notification
from . import bugsnag_extra
bugsnag.before_notify(add_flask_request_to_notification)
bugsnag.before_notify(bugsnag_extra.add_pillar_request_to_notification)
got_request_exception.connect(self.__notify_bugsnag)
self.log.info('Bugsnag setup complete')

51
pillar/bugsnag_extra.py Normal file
View File

@ -0,0 +1,51 @@
# Keys in the user's session dictionary that are removed before sending to Bugsnag.
SESSION_KEYS_TO_REMOVE = ('blender_id_oauth_token', 'user_id')
def add_pillar_request_to_notification(notification):
"""Adds request metadata to the Bugsnag notifications.
This basically copies bugsnag.flask.add_flask_request_to_notification,
but is altered to include Pillar-specific metadata.
"""
from flask import request, session
from bugsnag.wsgi import request_path
import pillar.auth
if not request:
return
notification.context = "%s %s" % (request.method,
request_path(request.environ))
if 'id' not in notification.user:
user: pillar.auth.UserClass = pillar.auth.current_user._get_current_object()
notification.set_user(id=user.user_id,
email=user.email,
name=user.username)
notification.user['roles'] = sorted(user.roles)
notification.user['capabilities'] = sorted(user.capabilities)
session_dict = dict(session)
for key in SESSION_KEYS_TO_REMOVE:
try:
del session_dict[key]
except KeyError:
pass
notification.add_tab("session", session_dict)
notification.add_tab("environment", dict(request.environ))
remote_addr = request.remote_addr
forwarded_for = request.headers.get('X-Forwarded-For')
if forwarded_for:
remote_addr = f'{forwarded_for} (proxied via {remote_addr})'
notification.add_tab("request", {
"url": request.base_url,
"headers": dict(request.headers),
"params": dict(request.form),
"data": {'request.data': request.data,
'request.json': request.get_json()},
"endpoint": request.endpoint,
"remote_addr": remote_addr,
})

View File

@ -63,6 +63,7 @@ GOOGLE_SITE_VERIFICATION = ''
ADMIN_USER_GROUP = '5596e975ea893b269af85c0e'
SUBSCRIBER_USER_GROUP = '5596e975ea893b269af85c0f'
BUGSNAG_API_KEY = ''
BUGSNAG_RELEASE_STAGE = 'development'
ALGOLIA_USER = '-SECRET-'
ALGOLIA_API_KEY = '-SECRET-'