Skip to content

Commit 254b7e7

Browse files
authored
feat(flask): Add sentry_trace() template helper (#1336)
To setup distributed tracing links between a Flask app and a front-end app, one needs to figure out how to get the current hub, safely get the traceparent and then properly pass it into a template and then finally use that properly in a `meta` tag. [The guide](https://docs.sentry.io/platforms/javascript/performance/connect-services/) is woefully inadequete and error-prone so this PR adds a built-in helper `sentry_trace()` to the Flask integration to simplfy this linking.
1 parent 64577a7 commit 254b7e7

File tree

3 files changed

+63
-11
lines changed

3 files changed

+63
-11
lines changed

examples/tracing/templates/index.html

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
<script src="https://browser.sentry-cdn.com/5.4.1/bundle.js" crossorigin="anonymous"></script>
1+
<meta name="sentry-trace" content="{{ sentry_trace }}" />
2+
<script src="https://browser.sentry-cdn.com/6.17.7/bundle.js" crossorigin="anonymous"></script>
3+
{{ sentry_trace }}
24
<!-- TODO: Replace with real tracing integration once it's fixed -->
35
<script src="/static/tracing.js" crossorigin="anonymous"></script>
46

@@ -14,14 +16,6 @@
1416
debug: true
1517
});
1618

17-
window.setTimeout(function() {
18-
const scope = Sentry.getCurrentHub().getScope();
19-
// TODO: Wait for Daniel's traceparent API
20-
scope.setSpan(scope.getSpan().constructor.fromTraceparent(
21-
"00-{{ traceparent['sentry-trace'].strip("-") }}-00"
22-
));
23-
});
24-
2519
async function compute() {
2620
const res = await fetch(
2721
"/compute/" +

sentry_sdk/integrations/flask.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@
2727

2828
try:
2929
from flask import ( # type: ignore
30+
Markup,
3031
Request,
3132
Flask,
3233
_request_ctx_stack,
3334
_app_ctx_stack,
3435
__version__ as FLASK_VERSION,
3536
)
3637
from flask.signals import (
38+
before_render_template,
3739
got_request_exception,
3840
request_started,
3941
)
@@ -77,6 +79,7 @@ def setup_once():
7779
if version < (0, 10):
7880
raise DidNotEnable("Flask 0.10 or newer is required.")
7981

82+
before_render_template.connect(_add_sentry_trace)
8083
request_started.connect(_request_started)
8184
got_request_exception.connect(_capture_exception)
8285

@@ -94,6 +97,23 @@ def sentry_patched_wsgi_app(self, environ, start_response):
9497
Flask.__call__ = sentry_patched_wsgi_app # type: ignore
9598

9699

100+
def _add_sentry_trace(sender, template, context, **extra):
101+
# type: (Flask, Any, Dict[str, Any], **Any) -> None
102+
103+
if "sentry_trace" in context:
104+
return
105+
106+
sentry_span = Hub.current.scope.span
107+
context["sentry_trace"] = (
108+
Markup(
109+
'<meta name="sentry-trace" content="%s" />'
110+
% (sentry_span.to_traceparent(),)
111+
)
112+
if sentry_span
113+
else ""
114+
)
115+
116+
97117
def _request_started(sender, **kwargs):
98118
# type: (Flask, **Any) -> None
99119
hub = Hub.current

tests/integrations/flask/test_flask.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@
66

77
flask = pytest.importorskip("flask")
88

9-
from flask import Flask, Response, request, abort, stream_with_context
9+
from flask import (
10+
Flask,
11+
Response,
12+
request,
13+
abort,
14+
stream_with_context,
15+
render_template_string,
16+
)
1017
from flask.views import View
1118

1219
from flask_login import LoginManager, login_user
@@ -365,7 +372,7 @@ def index():
365372
assert transaction_event["request"]["data"] == data
366373

367374

368-
@pytest.mark.parametrize("input_char", [u"a", b"a"])
375+
@pytest.mark.parametrize("input_char", ["a", b"a"])
369376
def test_flask_too_large_raw_request(sentry_init, input_char, capture_events, app):
370377
sentry_init(integrations=[flask_sentry.FlaskIntegration()], request_bodies="small")
371378

@@ -737,3 +744,34 @@ def dispatch_request(self):
737744

738745
assert event["message"] == "hi"
739746
assert event["transaction"] == "hello_class"
747+
748+
749+
def test_sentry_trace_context(sentry_init, app, capture_events):
750+
sentry_init(integrations=[flask_sentry.FlaskIntegration()])
751+
events = capture_events()
752+
753+
@app.route("/")
754+
def index():
755+
sentry_span = Hub.current.scope.span
756+
capture_message(sentry_span.to_traceparent())
757+
return render_template_string("{{ sentry_trace }}")
758+
759+
with app.test_client() as client:
760+
response = client.get("/")
761+
assert response.status_code == 200
762+
assert response.data.decode(
763+
"utf-8"
764+
) == '<meta name="sentry-trace" content="%s" />' % (events[0]["message"],)
765+
766+
767+
def test_dont_override_sentry_trace_context(sentry_init, app):
768+
sentry_init(integrations=[flask_sentry.FlaskIntegration()])
769+
770+
@app.route("/")
771+
def index():
772+
return render_template_string("{{ sentry_trace }}", sentry_trace="hi")
773+
774+
with app.test_client() as client:
775+
response = client.get("/")
776+
assert response.status_code == 200
777+
assert response.data == b"hi"

0 commit comments

Comments
 (0)