Skip to content

Commit 81e38e4

Browse files
committed
Fix issue with Flask instrumentation when a request spawn children threads and copie the request context
1 parent 7af87e1 commit 81e38e4

File tree

2 files changed

+12
-3
lines changed

2 files changed

+12
-3
lines changed

CHANGELOG.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2626
([#1618](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1618))
2727

2828
### Fixed
29-
29+
- Fix Flask instrumentation to only close the span if it was created by the same thread. `ValueError: generator already executing` ([#1654](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1654))
3030
- Fix TortoiseORM instrumentation `AttributeError: type object 'Config' has no attribute 'title'`
3131
([#1575](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1575))
3232
- Fix SQLAlchemy uninstrumentation

instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,8 @@ def response_hook(span: Span, status: str, response_headers: List):
238238
API
239239
---
240240
"""
241-
242241
from logging import getLogger
242+
from threading import get_ident
243243
from time import time_ns
244244
from timeit import default_timer
245245
from typing import Collection
@@ -265,6 +265,7 @@ def response_hook(span: Span, status: str, response_headers: List):
265265
_ENVIRON_STARTTIME_KEY = "opentelemetry-flask.starttime_key"
266266
_ENVIRON_SPAN_KEY = "opentelemetry-flask.span_key"
267267
_ENVIRON_ACTIVATION_KEY = "opentelemetry-flask.activation_key"
268+
_ENVIRON_THREAD_ID_KEY = "opentelemetry-flask.thread_id_key"
268269
_ENVIRON_TOKEN = "opentelemetry-flask.token"
269270

270271
_excluded_urls_from_env = get_excluded_urls("FLASK")
@@ -398,6 +399,7 @@ def _before_request():
398399
activation = trace.use_span(span, end_on_exit=True)
399400
activation.__enter__() # pylint: disable=E1101
400401
flask_request_environ[_ENVIRON_ACTIVATION_KEY] = activation
402+
flask_request_environ[_ENVIRON_THREAD_ID_KEY] = get_ident()
401403
flask_request_environ[_ENVIRON_SPAN_KEY] = span
402404
flask_request_environ[_ENVIRON_TOKEN] = token
403405

@@ -437,10 +439,17 @@ def _teardown_request(exc):
437439
return
438440

439441
activation = flask.request.environ.get(_ENVIRON_ACTIVATION_KEY)
440-
if not activation:
442+
thread_id = flask.request.environ.get(_ENVIRON_THREAD_ID_KEY)
443+
if not activation or thread_id != get_ident():
441444
# This request didn't start a span, maybe because it was created in
442445
# a way that doesn't run `before_request`, like when it is created
443446
# with `app.test_request_context`.
447+
#
448+
# Similarly, check the thread_id against the current thread to ensure
449+
# tear down only happens on the original thread. This situation can
450+
# arise if the original thread handling the request spawn children
451+
# threads and then uses something like copy_current_request_context
452+
# to copy the request context.
444453
return
445454
if exc is None:
446455
activation.__exit__(None, None, None)

0 commit comments

Comments
 (0)