Repository: open-telemetry/opentelemetry-python

Issue:
Handle "mixed mode" OT/OTel tracing in OT Bridge
@krnowak brought to my attention the following use case for the OT bridge:

- Library A is instrumented with OT and uses the OT bridge. Library A creates a parent span.
- Library B is instrumented with OTel directly. Library B creates a child span whose parent is the span library A created.

Do we want the OT bridge to support this use case by handling parent-child relationship between the wrapped OT span and the OTel span?


Discussion:
I think this would work out of the box with the straightforward implementation of basically delegating the OT functions to OTel mostly one to one. Or what do you think would break that interaction?
I don't know how storing of the active span is done in python, but in Go we use the `context.Context` to store the current span. So for the scenario described above, the OT bridge would take care of putting the OT span into the context to mark it as an active OT span, while the Otel tracer that gets the call from the bridge would setup the context with the Otel span as a current Otel span.

Other scenario was more "interesting" - library A uses Otel and then it calls library B that uses OT. So at this point it would be nice for the library B if the Otel span created by library A was also wrapped and put into the context as an active OT span. That way, an OT span created inside the library B would have a proper parent - a OT span that wraps an Otel span created in library A. The "interesting" part here is that library A uses Otel API, so no OT API is used directly and yet some OT stuff needs to happen too.
Looking at the OpenTelemetry code, I would implement a scope manager that sets / gets the active Span from the OpenTelemetry tracers (of course, setting is not possible ATM, #154 may help but you may need to add a new setter to the OTel Tracer).

EDIT: Regarding the type mismatch: Either you always return a new `OTAdaptedOTelSpan(otel_span)` or, if you think that might break someone, just monkeypatch a `_opentracing_span` property into the OTel span and reuse that.
Removing this from milestones for now, @carlosalberto to consider adding for v4.

Resolving patch:
diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/__init__.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-opentracing-shim/src/opentelemetry/instrumentation/opentracing_shim/__init__.py
@@ -570,6 +570,11 @@ def start_active_span(
             :class:`ScopeManagerShim`.
         """
 
+        current_span = get_current_span()
+
+        if child_of is None and current_span is not INVALID_SPAN_CONTEXT:
+            child_of = SpanShim(None, None, current_span)
+
         span = self.start_span(
             operation_name=operation_name,
             child_of=child_of,


Test patch:
diff --git a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_shim.py b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_shim.py
--- a/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_shim.py
+++ b/instrumentation/opentelemetry-instrumentation-opentracing-shim/tests/test_shim.py
@@ -557,7 +557,6 @@ def test_extract_binary(self):
             self.shim.extract(opentracing.Format.BINARY, bytearray())
 
     def test_baggage(self):
-        """Test SpanShim baggage being set and being immutable"""
 
         span_context_shim = SpanContextShim(
             trace.SpanContext(1234, 5678, is_remote=False)
@@ -592,3 +591,31 @@ def test_active(self):
 
         # Verify no span is active.
         self.assertIsNone(self.shim.active_span)
+
+    def test_mixed_mode(self):
+        """Test that span parent-child relationship is kept between
+        OpenTelemetry and the OpenTracing shim"""
+
+        span_shim = self.shim.start_span("TestSpan16")
+
+        with self.shim.scope_manager.activate(span_shim, finish_on_close=True):
+
+            with (
+                TracerProvider()
+                .get_tracer(__name__)
+                .start_as_current_span("abc")
+            ) as opentelemetry_span:
+
+                self.assertIs(
+                    span_shim.unwrap().context, opentelemetry_span.parent,
+                )
+
+        with (
+            TracerProvider().get_tracer(__name__).start_as_current_span("abc")
+        ) as opentelemetry_span:
+
+            with self.shim.start_active_span("TestSpan17") as scope:
+
+                self.assertIs(
+                    scope.span.unwrap().parent, opentelemetry_span.context,
+                )