Skip to content

Commit 9bcbf06

Browse files
stevenmirabitoliam-middlebrook
authored andcommitted
Add Sentry error reporting
1 parent a30da1c commit 9bcbf06

File tree

9 files changed

+99
-24
lines changed

9 files changed

+99
-24
lines changed

conditional/__init__.py

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
import os
22
import subprocess
3-
from flask import Flask, redirect, request
3+
from flask import Flask, redirect, request, render_template, g
44
from flask_sqlalchemy import SQLAlchemy
55
from flask_migrate import Migrate
66
from csh_ldap import CSHLDAP
7+
from raven import fetch_git_sha
8+
from raven.contrib.flask import Sentry
9+
from raven.exceptions import InvalidGitRepository
710
import structlog
811

912
app = Flask(__name__)
1013

11-
config = os.path.join(os.getcwd(), "config.py")
14+
config = os.path.join(app.config.get('ROOT_DIR', os.getcwd()), "config.py")
1215

1316
app.config.from_pyfile(config)
1417
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
1518

16-
app.config["GIT_REVISION"] = subprocess.check_output(['git',
17-
'rev-parse',
18-
'--short',
19-
'HEAD']).decode('utf-8').rstrip()
19+
try:
20+
app.config["GIT_REVISION"] = fetch_git_sha(app.config["ROOT_DIR"])[:7]
21+
except (InvalidGitRepository, KeyError):
22+
app.config["GIT_REVISION"] = "unknown"
23+
2024
db = SQLAlchemy(app)
2125
migrate = Migrate(app, db)
2226
logger = structlog.get_logger()
27+
sentry = Sentry(app)
2328

2429
ldap = CSHLDAP(app.config['LDAP_BIND_DN'],
2530
app.config['LDAP_BIND_PW'],
@@ -51,7 +56,6 @@
5156
app.register_blueprint(slideshow_bp)
5257
app.register_blueprint(cache_bp)
5358

54-
from conditional.util.flask import render_template
5559
from conditional.util.ldap import ldap_get_member
5660

5761
@app.route('/<path:path>')
@@ -67,16 +71,38 @@ def default_route():
6771
@app.errorhandler(404)
6872
@app.errorhandler(500)
6973
def route_errors(error):
70-
username = request.headers.get('x-webauth-user')
71-
member = ldap_get_member(username)
7274
data = dict()
73-
data['username'] = member.uid
74-
data['name'] = member.cn
75-
code = error.code
76-
return render_template(request=request,
77-
template_name='errors.html',
78-
error=str(error),
75+
username = request.headers.get('x-webauth-user')
76+
77+
# Handle the case where the header isn't present
78+
if username is not None:
79+
member = ldap_get_member(username)
80+
data['username'] = member.uid
81+
data['name'] = member.cn
82+
else:
83+
data['username'] = "unknown"
84+
data['name'] = "Unknown"
85+
86+
# Figure out what kind of error was passed
87+
if isinstance(error, int):
88+
code = error
89+
elif hasattr(error, 'code'):
90+
code = error.code
91+
else:
92+
# Unhandled exception
93+
code = 500
94+
95+
# Is this a 404?
96+
if code == 404:
97+
error_desc = "Page Not Found"
98+
else:
99+
error_desc = type(error).__name__
100+
101+
return render_template('errors.html',
102+
error=error_desc,
79103
error_code=code,
104+
event_id=g.sentry_event_id,
105+
public_dsn=sentry.client.get_public_dsn('https'),
80106
**data), int(code)
81107

82108
@app.cli.command()

conditional/templates/errors.html

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
{% extends "nav.html" %}
2-
{% block title %}
3-
{{ error_code }}
4-
{% endblock %}
2+
{% block title %}Error {{ error_code }}{% endblock %}
53
{% block body %}
64
<div class="container main error-page align-center">
75
<div class="col-xs-12">
86
<img src="/static/images/material_attention.svg" alt="Attention!">
97
<h1>Congratulations or I'm Sorry...</h1>
108
<h2>Something has gone terribly wrong!</h2>
11-
<h3>The following error has been reported:</h3>
12-
<p>{{ error }}</p>
9+
<h3>{{ error }}</h3>
10+
{% if event_id %}
11+
<p>The error identifier is: {{ event_id }}</p>
12+
{% endif %}
1313
</div>
14+
{% if event_id %}
15+
<button type="button" class="btn btn-primary" data-module="errorReport" data-event="{{ event_id }}">Report this Error</button>
16+
{% endif %}
1417
</div>
18+
1519
{% endblock %}

config.sample.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import os
2+
from raven import fetch_git_sha
23

34
# Flask config
45
DEBUG = True
6+
ROOT_DIR = os.path.dirname(__file__)
57
HOST_NAME = 'localhost'
68
APP_NAME = 'conditional'
79
IP = '0.0.0.0'
@@ -12,6 +14,13 @@
1214
LDAP_BIND_DN = 'cn=conditional,ou=Apps,dc=csh,dc=rit,dc=edu'
1315
LDAP_BIND_PW = ''
1416

17+
# Sentry config
18+
# Do not set the DSN for local development
19+
SENTRY_CONFIG = {
20+
'dsn': '',
21+
'release': fetch_git_sha(ROOT_DIR),
22+
}
23+
1524
# Database config
1625
SQLALCHEMY_DATABASE_URI = 'sqlite:///{}'.format(os.path.join(os.getcwd(), "data.db"))
1726
ZOO_DATABASE_URI = 'mysql+pymysql://user:pass@host/database'

frontend/javascript/app.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
// Install Raven to send errors to Sentry
2+
import Raven from 'raven-js';
3+
Raven
4+
.config('https://151ecfab1a8242009012d45a19064cfd@sentry.io/133175')
5+
.install();
6+
7+
// Capture unhandled exceptions in promises
8+
window.addEventListener('unhandledrejection', (err) => {
9+
Raven.captureException(err.reason);
10+
});
11+
12+
// Load the rest of the modules
113
import "jquery";
214
import "bootstrap";
315
import "./modules";
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import Raven from 'raven-js';
2+
3+
export default class ErrorReport {
4+
constructor(btn) {
5+
this.btn = btn;
6+
this.eventId = this.btn.dataset.event;
7+
this.render();
8+
}
9+
10+
render() {
11+
this.btn.addEventListener('click', () => this._invokeRavenModal());
12+
}
13+
14+
_invokeRavenModal() {
15+
Raven.showReportDialog({
16+
eventId: this.eventId
17+
});
18+
}
19+
}

frontend/stylesheets/pages/_errors.scss

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,16 @@
2020
}
2121

2222
h2 {
23-
margin-bottom: 80px;
23+
margin-bottom: 50px;
2424
font-size: 30px;
2525
}
2626

2727
h3 {
28-
margin: 0;
2928
font-size: 25px;
3029
}
3130

3231
p {
33-
font-size: 20px;
32+
font-size: 15px;
3433
font-weight: lighter;
3534
}
3635
}

frontend/stylesheets/partials/_global.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
.navbar-fixed-bottom, .navbar-fixed-top {
2+
z-index: 900;
3+
}
4+
15
.sub-header {
26
border-bottom: 1px solid #eee;
37
padding-bottom: 10px;
@@ -68,7 +72,7 @@ tr {
6872
.footer-version {
6973
display: block;
7074
opacity: .3;
71-
margin: 10px auto 20px;
75+
margin: 50px auto 20px;
7276
text-align: center;
7377
color: #000;
7478
font-size: .9em;

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
"object-assign": "4.1.0",
9090
"open": "0.0.5",
9191
"pretty-hrtime": "1.0.2",
92+
"raven-js": "^3.9.2",
9293
"require-dir": "0.3.0",
9394
"sinon": "1.17.4",
9495
"sinon-chai": "2.8.0",

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ flask_sqlalchemy
1010
flask_migrate
1111
pylint
1212
psycopg2
13+
raven[flask]

0 commit comments

Comments
 (0)