Skip to content

Commit 8ed3b4b

Browse files
authored
Merge pull request #365 from DataDog/andor44-master
Bring distributed tracing to Flask
2 parents 2f5ac8c + 3f7fbe9 commit 8ed3b4b

File tree

4 files changed

+38
-5
lines changed

4 files changed

+38
-5
lines changed

ddtrace/contrib/flask/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
1111
and create a `TraceMiddleware` object::
1212
13-
traced_app = TraceMiddleware(app, tracer, service="my-flask-app")
13+
traced_app = TraceMiddleware(app, tracer, service="my-flask-app", distributed_tracing=False)
1414
1515
Here is the end result, in a sample app::
1616
@@ -22,12 +22,14 @@
2222
2323
app = Flask(__name__)
2424
25-
traced_app = TraceMiddleware(app, tracer, service="my-flask-app")
25+
traced_app = TraceMiddleware(app, tracer, service="my-flask-app", distributed_tracing=False)
2626
2727
@app.route("/")
2828
def home():
2929
return "hello world"
3030
31+
Set `distributed_tracing=True` if this is called remotely from an instrumented application.
32+
We suggest to enable it only for internal services where headers are under your control.
3133
"""
3234

3335
from ..util import require_modules

ddtrace/contrib/flask/middleware.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# project
1212
from ... import compat
1313
from ...ext import http, errors, AppTypes
14+
from ...propagation.http import HTTPPropagator
1415

1516
# 3p
1617
import flask.templating
@@ -22,13 +23,14 @@
2223

2324
class TraceMiddleware(object):
2425

25-
def __init__(self, app, tracer, service="flask", use_signals=True):
26+
def __init__(self, app, tracer, service="flask", use_signals=True, distributed_tracing=False):
2627
self.app = app
2728
self.app.logger.info("initializing trace middleware")
2829

2930
# save our traces.
3031
self._tracer = tracer
3132
self._service = service
33+
self._use_distributed_tracing = distributed_tracing
3234

3335
self._tracer.set_service_info(
3436
service=service,
@@ -86,6 +88,12 @@ def _connect(self, signal_to_handler):
8688
# common methods
8789

8890
def _start_span(self):
91+
if self._use_distributed_tracing:
92+
propagator = HTTPPropagator()
93+
context = propagator.extract(request.headers)
94+
# Only need to active the new context if something was propagated
95+
if context.trace_id:
96+
self._tracer.context_provider.activate(context)
8997
try:
9098
g.flask_datadog_span = self._tracer.trace(
9199
"flask.request",
@@ -168,7 +176,6 @@ def _request_exception(self, *args, **kwargs):
168176
except Exception:
169177
self.app.logger.exception("error tracing error")
170178

171-
172179
def _patch_render(tracer):
173180
""" patch flask's render template methods with the given tracer. """
174181
# fall back to patching global method

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ For that, refer to the configuration of the given integration.
285285
Supported web frameworks:
286286

287287
- Django
288+
- Flask
288289

289290

290291
HTTP client/server

tests/contrib/flask/test_flask.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
# project
1313
from ddtrace import Tracer
14+
from ddtrace.constants import SAMPLING_PRIORITY_KEY
1415
from ddtrace.contrib.flask import TraceMiddleware
1516
from ddtrace.ext import http, errors
1617
from ...test_tracer import DummyWriter
@@ -91,7 +92,7 @@ def handle_my_exception(e):
9192
# work)
9293
service = "test.flask.service"
9394
assert not writer.pop() # should always be empty
94-
traced_app = TraceMiddleware(app, tracer, service=service)
95+
traced_app = TraceMiddleware(app, tracer, service=service, distributed_tracing=True)
9596

9697
# make the app testable
9798
app.config['TESTING'] = True
@@ -341,3 +342,25 @@ def test_404(self):
341342
eq_(s.meta.get(http.STATUS_CODE), '404')
342343
eq_(s.meta.get(http.METHOD), 'GET')
343344
eq_(s.meta.get(http.URL), u'http://localhost/404/üŋïĉóđē')
345+
346+
def test_propagation(self):
347+
rv = app.get('/', headers={
348+
'x-datadog-trace-id': '1234',
349+
'x-datadog-parent-id': '4567',
350+
'x-datadog-sampling-priority': '2'
351+
})
352+
353+
# ensure request worked
354+
eq_(rv.status_code, 200)
355+
eq_(rv.data, b'hello')
356+
357+
# ensure trace worked
358+
assert not tracer.current_span(), tracer.current_span().pprint()
359+
spans = writer.pop()
360+
eq_(len(spans), 1)
361+
s = spans[0]
362+
363+
# ensure the propagation worked well
364+
eq_(s.trace_id, 1234)
365+
eq_(s.parent_id, 4567)
366+
eq_(s.get_metric(SAMPLING_PRIORITY_KEY), 2)

0 commit comments

Comments
 (0)