Add sentry exception monitoring.

This commit is contained in:
Jake Moshenko 2014-04-28 18:59:22 -04:00
parent b30d0976f0
commit fe665118bb
8 changed files with 77 additions and 8 deletions

2
app.py
View file

@ -11,6 +11,7 @@ import features
from storage import Storage from storage import Storage
from data.userfiles import Userfiles from data.userfiles import Userfiles
from util.analytics import Analytics from util.analytics import Analytics
from util.exceptionlog import Sentry
from data.billing import Billing from data.billing import Billing
@ -44,3 +45,4 @@ storage = Storage(app)
userfiles = Userfiles(app) userfiles = Userfiles(app)
analytics = Analytics(app) analytics = Analytics(app)
billing = Billing(app) billing = Billing(app)
sentry = Sentry(app)

View file

@ -32,7 +32,7 @@ def logs_init_builder(level=logging.DEBUG,
# values are set to the frontend, DO NOT PLACE ANY SECRETS OR KEYS in this list. # values are set to the frontend, DO NOT PLACE ANY SECRETS OR KEYS in this list.
CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'GITHUB_CLIENT_ID', CLIENT_WHITELIST = ['SERVER_HOSTNAME', 'PREFERRED_URL_SCHEME', 'GITHUB_CLIENT_ID',
'GITHUB_LOGIN_CLIENT_ID', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY', 'GITHUB_LOGIN_CLIENT_ID', 'MIXPANEL_KEY', 'STRIPE_PUBLISHABLE_KEY',
'ENTERPRISE_LOGO_URL'] 'ENTERPRISE_LOGO_URL', 'SENTRY_PUBLIC_DSN']
def getFrontendVisibleConfig(config_dict): def getFrontendVisibleConfig(config_dict):
@ -102,6 +102,11 @@ class DefaultConfig(object):
# Analytics # Analytics
ANALYTICS_TYPE = "FakeAnalytics" ANALYTICS_TYPE = "FakeAnalytics"
# Exception logging
EXCEPTION_LOG_TYPE = 'FakeSentry'
SENTRY_DSN = None
SENTRY_PUBLIC_DSN = None
# Github Config # Github Config
GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token' GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token'
GITHUB_USER_URL = 'https://api.github.com/user' GITHUB_USER_URL = 'https://api.github.com/user'

View file

@ -155,6 +155,7 @@ def render_page_template(name, **kwargs):
feature_set=json.dumps(features.get_features()), feature_set=json.dumps(features.get_features()),
config_set=json.dumps(getFrontendVisibleConfig(app.config)), config_set=json.dumps(getFrontendVisibleConfig(app.config)),
mixpanel_key=app.config.get('MIXPANEL_KEY', ''), mixpanel_key=app.config.get('MIXPANEL_KEY', ''),
sentry_public_dsn=app.config.get('SENTRY_PUBLIC_DSN', ''),
is_debug=str(app.config.get('DEBUGGING', False)).lower(), is_debug=str(app.config.get('DEBUGGING', False)).lower(),
show_chat=features.OLARK_CHAT, show_chat=features.OLARK_CHAT,
cache_buster=cache_buster, cache_buster=cache_buster,

View file

@ -30,3 +30,6 @@ git+https://github.com/NateFerrero/oauth2lib.git
alembic alembic
sqlalchemy sqlalchemy
python-magic python-magic
reportlab==2.7
blinker
raven

View file

@ -6,10 +6,11 @@ Flask-Principal==0.4.0
Flask-RESTful==0.2.12 Flask-RESTful==0.2.12
Jinja2==2.7.2 Jinja2==2.7.2
Mako==0.9.1 Mako==0.9.1
MarkupSafe==0.19 MarkupSafe==0.21
Pillow==2.4.0 Pillow==2.4.0
PyGithub==1.24.1 PyGithub==1.24.1
PyMySQL==0.6.1 PyMySQL==0.6.2
PyPDF2==1.21
SQLAlchemy==0.9.4 SQLAlchemy==0.9.4
Werkzeug==0.9.4 Werkzeug==0.9.4
alembic==0.6.4 alembic==0.6.4
@ -23,8 +24,8 @@ ecdsa==0.11
gevent==1.0 gevent==1.0
greenlet==0.4.2 greenlet==0.4.2
gunicorn==18.0 gunicorn==18.0
hiredis==0.1.2 hiredis==0.1.3
html5lib==1.0b3 html5lib==0.999
itsdangerous==0.24 itsdangerous==0.24
jsonschema==2.3.0 jsonschema==2.3.0
lockfile==0.9.1 lockfile==0.9.1
@ -35,15 +36,15 @@ mixpanel-py==3.1.2
mock==1.0.1 mock==1.0.1
git+https://github.com/NateFerrero/oauth2lib.git git+https://github.com/NateFerrero/oauth2lib.git
paramiko==1.13.0 paramiko==1.13.0
peewee==2.2.2 peewee==2.2.3
py-bcrypt==0.4 py-bcrypt==0.4
pyPdf==1.13
pycrypto==2.6.1 pycrypto==2.6.1
python-daemon==1.6 python-daemon==1.6
python-dateutil==2.2 python-dateutil==2.2
python-digitalocean==0.7 python-digitalocean==0.7
python-magic==0.4.6 python-magic==0.4.6
pytz==2014.2 pytz==2014.2
raven==4.2.1
redis==2.9.1 redis==2.9.1
reportlab==2.7 reportlab==2.7
requests==2.2.1 requests==2.2.1
@ -51,4 +52,4 @@ six==1.6.1
stripe==1.14.0 stripe==1.14.0
websocket-client==0.11.0 websocket-client==0.11.0
wsgiref==0.1.2 wsgiref==0.1.2
xhtml2pdf==0.0.5 xhtml2pdf==0.0.6

View file

@ -778,7 +778,18 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
olark('api.chat.updateVisitorStatus', {snippet: 'username: ' + userResponse.username}); olark('api.chat.updateVisitorStatus', {snippet: 'username: ' + userResponse.username});
} }
if (window.Raven !== undefined) {
Raven.setUser({
email: userResponse.email,
id: userResponse.username
});
}
CookieService.putPermanent('quay.loggedin', 'true'); CookieService.putPermanent('quay.loggedin', 'true');
} else {
if (window.Raven !== undefined) {
Raven.setUser();
}
} }
if (opt_callback) { if (opt_callback) {
@ -1366,6 +1377,17 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
}]); }]);
} }
if (window.__config && window.__config.SENTRY_PUBLIC_DSN) {
quayApp.config(function($provide) {
$provide.decorator("$exceptionHandler", function($delegate) {
return function(ex, cause) {
$delegate(ex, cause);
Raven.captureException(ex, {extra: {cause: cause}});
};
});
});
}
function buildConditionalLinker($animate, name, evaluator) { function buildConditionalLinker($animate, name, evaluator) {
// Based off of a solution found here: http://stackoverflow.com/questions/20325480/angularjs-whats-the-best-practice-to-add-ngif-to-a-directive-programmatically // Based off of a solution found here: http://stackoverflow.com/questions/20325480/angularjs-whats-the-best-practice-to-add-ngif-to-a-directive-programmatically

View file

@ -58,6 +58,7 @@
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.2.0/js/bootstrap-datepicker.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-datepicker/1.2.0/js/bootstrap-datepicker.min.js"></script>
<script src="//cdn.jsdelivr.net/g/bootbox@4.1.0,underscorejs@1.5.2,restangular@1.2.0,d3js@3.3.3,momentjs"></script> <script src="//cdn.jsdelivr.net/g/bootbox@4.1.0,underscorejs@1.5.2,restangular@1.2.0,d3js@3.3.3,momentjs"></script>
<script src="//cdn.ravenjs.com/1.1.14/jquery,native/raven.min.js"></script>
<script src="https://checkout.stripe.com/checkout.js"></script> <script src="https://checkout.stripe.com/checkout.js"></script>
@ -73,6 +74,12 @@
<script src="/static/{{ script_path }}?v={{ cache_buster }}"></script> <script src="/static/{{ script_path }}?v={{ cache_buster }}"></script>
{% endfor %} {% endfor %}
{% if sentry_public_dsn %}
<script type="text/javascript">
Raven.config('{{ sentry_public_dsn }}').install();
</script>
{% endif %}
{% if mixpanel_key %} {% if mixpanel_key %}
<!-- start Mixpanel --><script type="text/javascript"> <!-- start Mixpanel --><script type="text/javascript">
(function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f);b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!== (function(e,b){if(!b.__SV){var a,f,i,g;window.mixpanel=b;a=e.createElement("script");a.type="text/javascript";a.async=!0;a.src=("https:"===e.location.protocol?"https:":"http:")+'//cdn.mxpnl.com/libs/mixpanel-2.2.min.js';f=e.getElementsByTagName("script")[0];f.parentNode.insertBefore(a,f);b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}var c=b;"undefined"!==

28
util/exceptionlog.py Normal file
View file

@ -0,0 +1,28 @@
from raven.contrib.flask import Sentry as FlaskSentry
class FakeSentry(object):
pass
class Sentry(object):
def __init__(self, app=None):
self.app = app
if app is not None:
self.state = self.init_app(app)
else:
self.state = None
def init_app(self, app):
sentry_type = app.config.get('EXCEPTION_LOG_TYPE', 'FakeSentry')
if sentry_type == 'Sentry':
sentry = FlaskSentry(app)
else:
sentry = FakeSentry()
# register extension with app
app.extensions = getattr(app, 'extensions', {})
app.extensions['sentry'] = sentry
return sentry
def __getattr__(self, name):
return getattr(self.state, name, None)