Repository: open-telemetry/opentelemetry-python

Issue:
Update opentracing shim to map errors from logs
As per spec:
>The Add Event's name parameter MUST be the value with the event key in the pair set, or else fallback to use the log literal string.

> If pair set contains a event=error entry, the values MUST be mapped from OpenTracing Log Fields to an Event with the conventions outlined in the Exception semantic conventions document:


https://github.com/open-telemetry/opentelemetry-specification/pull/1563


Discussion:

Resolving patch:
diff --git a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py
--- a/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py
+++ b/shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py
@@ -86,7 +86,8 @@
 # pylint:disable=no-member
 
 import logging
-from typing import Optional, TypeVar, Union
+from types import TracebackType
+from typing import Optional, Type, TypeVar, Union
 
 from deprecated import deprecated
 from opentracing import (
@@ -401,16 +402,24 @@ def close(self):
             ends the associated span**, regardless of the value passed in
             *finish_on_close* when activating the span.
         """
+        self._end_span_scope(None, None, None)
 
-        detach(self._token)
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        """
+        Override the __exit__ method of `opentracing.scope.Scope` so we can report
+        exceptions correctly in opentelemetry specification format.
+        """
+        self._end_span_scope(exc_type, exc_val, exc_tb)
 
+    def _end_span_scope(
+        self,
+        exc_type: Optional[Type[BaseException]],
+        exc_val: Optional[BaseException],
+        exc_tb: Optional[TracebackType],
+    ) -> None:
+        detach(self._token)
         if self._span_cm is not None:
-            # We don't have error information to pass to `__exit__()` so we
-            # pass `None` in all arguments. If the OpenTelemetry tracer
-            # implementation requires this information, the `__exit__()` method
-            # on `opentracing.Scope` should be overridden and modified to pass
-            # the relevant values to this `close()` method.
-            self._span_cm.__exit__(None, None, None)
+            self._span_cm.__exit__(exc_type, exc_val, exc_tb)
         else:
             self._span.unwrap().end()
 


Test patch:
diff --git a/shim/opentelemetry-opentracing-shim/tests/test_shim.py b/shim/opentelemetry-opentracing-shim/tests/test_shim.py
--- a/shim/opentelemetry-opentracing-shim/tests/test_shim.py
+++ b/shim/opentelemetry-opentracing-shim/tests/test_shim.py
@@ -16,6 +16,7 @@
 # pylint:disable=no-member
 
 import time
+import traceback
 from unittest import TestCase
 from unittest.mock import Mock
 
@@ -475,12 +476,30 @@ def test_span_on_error(self):
         """
 
         # Raise an exception while a span is active.
-        with self.assertRaises(Exception):
+        with self.assertRaises(Exception) as exc_ctx:
             with self.shim.start_active_span("TestName") as scope:
-                raise Exception
+                raise Exception("bad thing")
 
+        ex = exc_ctx.exception
+        expected_stack = "".join(
+            traceback.format_exception(
+                etype=type(ex), value=ex, tb=ex.__traceback__
+            )
+        )
         # Verify exception details have been added to span.
-        self.assertEqual(scope.span.unwrap().attributes["error"], True)
+        exc_event = scope.span.unwrap().events[0]
+
+        self.assertEqual(exc_event.name, "exception")
+        self.assertEqual(
+            exc_event.attributes["exception.message"], "bad thing"
+        )
+        self.assertEqual(
+            exc_event.attributes["exception.type"], Exception.__name__
+        )
+        # cannot get the whole stacktrace so just assert exception part is contained
+        self.assertIn(
+            expected_stack, exc_event.attributes["exception.stacktrace"]
+        )
 
     def test_inject_http_headers(self):
         """Test `inject()` method for Format.HTTP_HEADERS."""