Repository: open-telemetry/opentelemetry-python-contrib

Issue:
Flask: Conditionally create SERVER spans
Part of #445


Discussion:
@owais  I would like to work on this issue as a beginner. Kindly let me know if it's okay to pick this issue or not 🙂 
Hi @pravarag. Yes, you can pick it up but be warned that it might sound simpler than it is. We've had at least one other person attempt this only to find out it was more complicated than it looked :) 

I don't want to discourage you but just trying to set the expectations. I've removed "good first issue" label from this ticket as it was a bit misleading I think.

All things considered, this is not a hard or complex issue but definitely not a byte-sized, beginner friendly problem either. Happy to assign it to you.

Thanks @owais , if possible could you please help me in finding a beginner friendly issue to work on 🙂 , I'll be happy to take that.
https://github.com/open-telemetry/opentelemetry-python-contrib/labels/good%20first%20issue
https://github.com/open-telemetry/opentelemetry-python/labels/good%20first%20issue

@pravarag ^
Hi @owais 
I would like to work on this as my first issue! Can you assign this issue to me?
Would really appreciate any help in getting started with the same.
Done 

Resolving patch:
diff --git a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/__init__.py
@@ -176,15 +176,24 @@ def _before_request():
             return
         flask_request_environ = flask.request.environ
         span_name = get_default_span_name()
-        token = context.attach(
-            extract(flask_request_environ, getter=otel_wsgi.wsgi_getter)
-        )
+
+        token = ctx = span_kind = None
+
+        if trace.get_current_span() is trace.INVALID_SPAN:
+            ctx = extract(flask_request_environ, getter=otel_wsgi.wsgi_getter)
+            token = context.attach(ctx)
+            span_kind = trace.SpanKind.SERVER
+        else:
+            ctx = context.get_current()
+            span_kind = trace.SpanKind.INTERNAL
 
         span = tracer.start_span(
             span_name,
-            kind=trace.SpanKind.SERVER,
+            ctx,
+            kind=span_kind,
             start_time=flask_request_environ.get(_ENVIRON_STARTTIME_KEY),
         )
+
         if request_hook:
             request_hook(span, flask_request_environ)
 
@@ -229,7 +238,9 @@ def _teardown_request(exc):
             activation.__exit__(
                 type(exc), exc, getattr(exc, "__traceback__", None)
             )
-        context.detach(flask.request.environ.get(_ENVIRON_TOKEN))
+
+        if flask.request.environ.get(_ENVIRON_TOKEN, None):
+            context.detach(flask.request.environ.get(_ENVIRON_TOKEN))
 
     return _teardown_request
 


Test patch:
diff --git a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py
--- a/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py
+++ b/instrumentation/opentelemetry-instrumentation-flask/tests/test_programmatic.py
@@ -23,6 +23,7 @@
     get_global_response_propagator,
     set_global_response_propagator,
 )
+from opentelemetry.instrumentation.wsgi import OpenTelemetryMiddleware
 from opentelemetry.sdk.resources import Resource
 from opentelemetry.semconv.trace import SpanAttributes
 from opentelemetry.test.test_base import TestBase
@@ -413,3 +414,31 @@ def test_custom_span_name(self):
             span_list[0].resource.attributes["service.name"],
             "flask-api-no-app",
         )
+
+
+class TestProgrammaticWrappedWithOtherFramework(
+    InstrumentationTest, TestBase, WsgiTestBase
+):
+    def setUp(self):
+        super().setUp()
+
+        self.app = Flask(__name__)
+        self.app.wsgi_app = OpenTelemetryMiddleware(self.app.wsgi_app)
+        FlaskInstrumentor().instrument_app(self.app)
+        self._common_initialization()
+
+    def tearDown(self) -> None:
+        super().tearDown()
+        with self.disable_logging():
+            FlaskInstrumentor().uninstrument_app(self.app)
+
+    def test_mark_span_internal_in_presence_of_span_from_other_framework(self):
+        resp = self.client.get("/hello/123")
+        self.assertEqual(200, resp.status_code)
+        resp.get_data()
+        span_list = self.memory_exporter.get_finished_spans()
+        self.assertEqual(trace.SpanKind.INTERNAL, span_list[0].kind)
+        self.assertEqual(trace.SpanKind.SERVER, span_list[1].kind)
+        self.assertEqual(
+            span_list[0].parent.span_id, span_list[1].context.span_id
+        )