forked from getsentry/sentry-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathflask.py
More file actions
177 lines (132 loc) · 5.12 KB
/
Copy pathflask.py
File metadata and controls
177 lines (132 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
from __future__ import absolute_import
import weakref
from sentry_sdk.hub import Hub, _should_send_default_pii
from sentry_sdk.utils import capture_internal_exceptions, event_from_exception
from sentry_sdk.integrations import Integration
from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware
from sentry_sdk.integrations._wsgi_common import RequestExtractor
try:
import flask_login
except ImportError:
flask_login = None
from flask import Flask, _request_ctx_stack, _app_ctx_stack
from flask.signals import (
appcontext_pushed,
appcontext_tearing_down,
got_request_exception,
request_started,
)
class FlaskIntegration(Integration):
identifier = "flask"
transaction_style = None
def __init__(self, transaction_style="endpoint"):
TRANSACTION_STYLE_VALUES = ("endpoint", "url")
if transaction_style not in TRANSACTION_STYLE_VALUES:
raise ValueError(
"Invalid value for transaction_style: %s (must be in %s)"
% (transaction_style, TRANSACTION_STYLE_VALUES)
)
self.transaction_style = transaction_style
@staticmethod
def setup_once():
appcontext_pushed.connect(_push_appctx)
appcontext_tearing_down.connect(_pop_appctx)
request_started.connect(_request_started)
got_request_exception.connect(_capture_exception)
old_app = Flask.__call__
def sentry_patched_wsgi_app(self, environ, start_response):
if Hub.current.get_integration(FlaskIntegration) is None:
return old_app(self, environ, start_response)
return SentryWsgiMiddleware(lambda *a, **kw: old_app(self, *a, **kw))(
environ, start_response
)
Flask.__call__ = sentry_patched_wsgi_app
def _push_appctx(*args, **kwargs):
hub = Hub.current
if hub.get_integration(FlaskIntegration) is not None:
# always want to push scope regardless of whether WSGI app might already
# have (not the case for CLI for example)
hub.push_scope()
def _pop_appctx(*args, **kwargs):
hub = Hub.current
if hub.get_integration(FlaskIntegration) is not None:
hub.pop_scope_unsafe()
def _request_started(sender, **kwargs):
hub = Hub.current
integration = hub.get_integration(FlaskIntegration)
if integration is None:
return
weak_request = weakref.ref(_request_ctx_stack.top.request)
app = _app_ctx_stack.top.app
with hub.configure_scope() as scope:
scope.add_event_processor(
_make_request_event_processor(app, weak_request, integration)
)
class FlaskRequestExtractor(RequestExtractor):
def url(self):
return "%s://%s%s" % (self.request.scheme, self.request.host, self.request.path)
def env(self):
return self.request.environ
def cookies(self):
return self.request.cookies
def raw_data(self):
return self.request.data
def form(self):
return self.request.form
def files(self):
return self.request.files
def size_of_file(self, file):
return file.content_length
def _make_request_event_processor(app, weak_request, integration):
def inner(event, hint):
request = weak_request()
# if the request is gone we are fine not logging the data from
# it. This might happen if the processor is pushed away to
# another thread.
if request is None:
return event
if "transaction" not in event:
try:
if integration.transaction_style == "endpoint":
event["transaction"] = request.url_rule.endpoint
elif integration.transaction_style == "url":
event["transaction"] = request.url_rule.rule
except Exception:
pass
with capture_internal_exceptions():
FlaskRequestExtractor(request).extract_into_event(event)
if _should_send_default_pii():
with capture_internal_exceptions():
_add_user_to_event(event)
return event
return inner
def _capture_exception(sender, exception, **kwargs):
hub = Hub.current
if hub.get_integration(FlaskIntegration) is None:
return
event, hint = event_from_exception(
exception,
client_options=hub.client.options,
mechanism={"type": "flask", "handled": False},
)
hub.capture_event(event, hint=hint)
def _add_user_to_event(event):
if flask_login is None:
return
user = flask_login.current_user
if user is None:
return
with capture_internal_exceptions():
# Access this object as late as possible as accessing the user
# is relatively costly
user_info = event.setdefault("user", {})
if "id" not in user_info:
try:
user_info["id"] = user.get_id()
# TODO: more configurable user attrs here
except AttributeError:
# might happen if:
# - flask_login could not be imported
# - flask_login is not configured
# - no user is logged in
pass