Repository: open-telemetry/opentelemetry-python-contrib

Issue:
Tornado instrumetnation status_code 4ever 500 if error exsist
Here https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py#L372-L379, when span finished, status_code is setting to 500, if error exists and status code from handler not equals 404. 
Is it correct behavior ?
I see, it needed for setting description in Status. But, in this method, if status_code <=499, Status must be set to UNSET. In our case, if status_code <=499 will set ERROR. https://github.com/open-telemetry/opentelemetry-python-contrib/blob/dbb35a29465bcaf84380715c356726718724859b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py#L62-L63.
Maybe needed extend this condition https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py#L372  to all 4xx http codes instead of just 404? 



Discussion:
@nicholasgribanov It was unclear from the description what is the problem here? Can you explain what is the issue and how does the proposed solution fixes the issue?
@srikanthccv thank's for a fast answer. 
In case, if I want to sampling spans on collector-level by processing with like this config:   

      [
        {
          name: error_policy,
          type: numeric_attribute,
          numeric_attribute: { key: "http.status_code", min_value: 500, max_value: 599 }
        }
      ]
I wait spans only with http-status more when 500. But, now, it doesn't work. Beacause, on the Tornado instrumentation level, server spans with error and http-status not equal 404, will forcibly setting http.status_code to 500 https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py#L372-L379.
 You can see this here:
![Снимок экрана от 2022-03-17 11-14-30](https://user-images.githubusercontent.com/33231210/158958492-10d6046b-ce4a-4467-be55-528acb72f433.png)

It's not right behavior. If you are waiting spans onlyf with http-status more than 500, you must not getting spans with 400 error. 
I see, it needed for setting description in Status. But, in this method, if status_code <=499, Status must be set to UNSET. In our case, if status_code <=499 will set ERROR. https://github.com/open-telemetry/opentelemetry-python-contrib/blob/dbb35a29465bcaf84380715c356726718724859b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/utils.py#L62-L63 I propose fix this and not change http.status_code, only for setting description message. Now I see, fix from my previusly message not right. Needed find way set descpription of error and not chage http.status_code.
Ok, I see there is a discrepancy. I actually wonder why do we change the status code from 4XX to 500, @owais, do you have any input?
Any updates ?

Resolving patch:
diff --git a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py
@@ -420,14 +420,15 @@ def _finish_span(tracer, handler, error=None):
             status_code = error.status_code
             if not ctx and status_code == 404:
                 ctx = _start_span(tracer, handler, _time_ns())
-        if status_code != 404:
+        else:
+            status_code = 500
+            reason = None
+        if status_code >= 500:
             finish_args = (
                 type(error),
                 error,
                 getattr(error, "__traceback__", None),
             )
-            status_code = 500
-            reason = None
 
     if not ctx:
         return


Test patch:
diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py
--- a/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py
+++ b/instrumentation/opentelemetry-instrumentation-tornado/tests/test_instrumentation.py
@@ -317,6 +317,41 @@ def test_404(self):
             },
         )
 
+    def test_http_error(self):
+        response = self.fetch("/raise_403")
+        self.assertEqual(response.code, 403)
+
+        spans = self.sorted_spans(self.memory_exporter.get_finished_spans())
+        self.assertEqual(len(spans), 2)
+        server, client = spans
+
+        self.assertEqual(server.name, "RaiseHTTPErrorHandler.get")
+        self.assertEqual(server.kind, SpanKind.SERVER)
+        self.assertSpanHasAttributes(
+            server,
+            {
+                SpanAttributes.HTTP_METHOD: "GET",
+                SpanAttributes.HTTP_SCHEME: "http",
+                SpanAttributes.HTTP_HOST: "127.0.0.1:"
+                + str(self.get_http_port()),
+                SpanAttributes.HTTP_TARGET: "/raise_403",
+                SpanAttributes.HTTP_CLIENT_IP: "127.0.0.1",
+                SpanAttributes.HTTP_STATUS_CODE: 403,
+                "tornado.handler": "tests.tornado_test_app.RaiseHTTPErrorHandler",
+            },
+        )
+
+        self.assertEqual(client.name, "GET")
+        self.assertEqual(client.kind, SpanKind.CLIENT)
+        self.assertSpanHasAttributes(
+            client,
+            {
+                SpanAttributes.HTTP_URL: self.get_url("/raise_403"),
+                SpanAttributes.HTTP_METHOD: "GET",
+                SpanAttributes.HTTP_STATUS_CODE: 403,
+            },
+        )
+
     def test_dynamic_handler(self):
         response = self.fetch("/dyna")
         self.assertEqual(response.code, 404)
diff --git a/instrumentation/opentelemetry-instrumentation-tornado/tests/tornado_test_app.py b/instrumentation/opentelemetry-instrumentation-tornado/tests/tornado_test_app.py
--- a/instrumentation/opentelemetry-instrumentation-tornado/tests/tornado_test_app.py
+++ b/instrumentation/opentelemetry-instrumentation-tornado/tests/tornado_test_app.py
@@ -105,6 +105,11 @@ def get(self):
         self.set_status(200)
 
 
+class RaiseHTTPErrorHandler(tornado.web.RequestHandler):
+    def get(self):
+        raise tornado.web.HTTPError(403)
+
+
 def make_app(tracer):
     app = tornado.web.Application(
         [
@@ -116,6 +121,7 @@ def make_app(tracer):
             (r"/healthz", HealthCheckHandler),
             (r"/ping", HealthCheckHandler),
             (r"/test_custom_response_headers", CustomResponseHeaderHandler),
+            (r"/raise_403", RaiseHTTPErrorHandler),
         ]
     )
     app.tracer = tracer