mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-28 20:52:57 +08:00
Added recording of exceptions in Pyramid (#2622)
This commit is contained in:
@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- `opentelemetry-instrumentation-pyramid` Record exceptions raised when serving a request
|
||||||
|
([#2622](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2622))
|
||||||
- `opentelemetry-sdk-extension-aws` Add AwsXrayLambdaPropagator
|
- `opentelemetry-sdk-extension-aws` Add AwsXrayLambdaPropagator
|
||||||
([#2573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2573))
|
([#2573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2573))
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ from opentelemetry.instrumentation.utils import _start_internal_or_server_span
|
|||||||
from opentelemetry.metrics import get_meter
|
from opentelemetry.metrics import get_meter
|
||||||
from opentelemetry.semconv.metrics import MetricInstruments
|
from opentelemetry.semconv.metrics import MetricInstruments
|
||||||
from opentelemetry.semconv.trace import SpanAttributes
|
from opentelemetry.semconv.trace import SpanAttributes
|
||||||
|
from opentelemetry.trace.status import Status, StatusCode
|
||||||
from opentelemetry.util.http import get_excluded_urls
|
from opentelemetry.util.http import get_excluded_urls
|
||||||
|
|
||||||
TWEEN_NAME = "opentelemetry.instrumentation.pyramid.trace_tween_factory"
|
TWEEN_NAME = "opentelemetry.instrumentation.pyramid.trace_tween_factory"
|
||||||
@ -180,6 +181,7 @@ def trace_tween_factory(handler, registry):
|
|||||||
|
|
||||||
response = None
|
response = None
|
||||||
status = None
|
status = None
|
||||||
|
recordable_exc = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = handler(request)
|
response = handler(request)
|
||||||
@ -190,11 +192,14 @@ def trace_tween_factory(handler, registry):
|
|||||||
# As described in docs, Pyramid exceptions are all valid
|
# As described in docs, Pyramid exceptions are all valid
|
||||||
# response types
|
# response types
|
||||||
response = exc
|
response = exc
|
||||||
|
if isinstance(exc, HTTPServerError):
|
||||||
|
recordable_exc = exc
|
||||||
raise
|
raise
|
||||||
except BaseException:
|
except BaseException as exc:
|
||||||
# In the case that a non-HTTPException is bubbled up we
|
# In the case that a non-HTTPException is bubbled up we
|
||||||
# should infer a internal server error and raise
|
# should infer a internal server error and raise
|
||||||
status = "500 InternalServerError"
|
status = "500 InternalServerError"
|
||||||
|
recordable_exc = exc
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
duration = max(round((default_timer() - start) * 1000), 0)
|
duration = max(round((default_timer() - start) * 1000), 0)
|
||||||
@ -222,6 +227,12 @@ def trace_tween_factory(handler, registry):
|
|||||||
getattr(response, "headerlist", None),
|
getattr(response, "headerlist", None),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if recordable_exc is not None:
|
||||||
|
span.set_status(
|
||||||
|
Status(StatusCode.ERROR, str(recordable_exc))
|
||||||
|
)
|
||||||
|
span.record_exception(recordable_exc)
|
||||||
|
|
||||||
if span.is_recording() and span.kind == trace.SpanKind.SERVER:
|
if span.is_recording() and span.kind == trace.SpanKind.SERVER:
|
||||||
custom_attributes = (
|
custom_attributes = (
|
||||||
otel_wsgi.collect_custom_response_headers_attributes(
|
otel_wsgi.collect_custom_response_headers_attributes(
|
||||||
|
@ -35,7 +35,7 @@ class InstrumentationTest:
|
|||||||
if helloid == 204:
|
if helloid == 204:
|
||||||
raise exc.HTTPNoContent()
|
raise exc.HTTPNoContent()
|
||||||
if helloid == 900:
|
if helloid == 900:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError("error message")
|
||||||
return Response("Hello: " + str(helloid))
|
return Response("Hello: " + str(helloid))
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -121,6 +121,7 @@ class TestAutomatic(InstrumentationTest, WsgiTestBase):
|
|||||||
span_list = self.memory_exporter.get_finished_spans()
|
span_list = self.memory_exporter.get_finished_spans()
|
||||||
self.assertEqual(len(span_list), 1)
|
self.assertEqual(len(span_list), 1)
|
||||||
self.assertEqual(span_list[0].status.status_code, StatusCode.UNSET)
|
self.assertEqual(span_list[0].status.status_code, StatusCode.UNSET)
|
||||||
|
self.assertEqual(len(span_list[0].events), 0)
|
||||||
|
|
||||||
PyramidInstrumentor().uninstrument()
|
PyramidInstrumentor().uninstrument()
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ from opentelemetry.instrumentation.propagators import (
|
|||||||
set_global_response_propagator,
|
set_global_response_propagator,
|
||||||
)
|
)
|
||||||
from opentelemetry.instrumentation.pyramid import PyramidInstrumentor
|
from opentelemetry.instrumentation.pyramid import PyramidInstrumentor
|
||||||
|
from opentelemetry.semconv.attributes import exception_attributes
|
||||||
from opentelemetry.semconv.trace import SpanAttributes
|
from opentelemetry.semconv.trace import SpanAttributes
|
||||||
from opentelemetry.test.wsgitestutil import WsgiTestBase
|
from opentelemetry.test.wsgitestutil import WsgiTestBase
|
||||||
from opentelemetry.util.http import get_excluded_urls
|
from opentelemetry.util.http import get_excluded_urls
|
||||||
@ -149,6 +150,7 @@ class TestProgrammatic(InstrumentationTest, WsgiTestBase):
|
|||||||
self.assertEqual(span_list[0].name, "POST /bye")
|
self.assertEqual(span_list[0].name, "POST /bye")
|
||||||
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
|
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
|
||||||
self.assertEqual(span_list[0].attributes, expected_attrs)
|
self.assertEqual(span_list[0].attributes, expected_attrs)
|
||||||
|
self.assertEqual(len(span_list[0].events), 0)
|
||||||
|
|
||||||
def test_internal_error(self):
|
def test_internal_error(self):
|
||||||
expected_attrs = expected_attributes(
|
expected_attrs = expected_attributes(
|
||||||
@ -166,6 +168,18 @@ class TestProgrammatic(InstrumentationTest, WsgiTestBase):
|
|||||||
self.assertEqual(span_list[0].name, "/hello/{helloid}")
|
self.assertEqual(span_list[0].name, "/hello/{helloid}")
|
||||||
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
|
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
|
||||||
self.assertEqual(span_list[0].attributes, expected_attrs)
|
self.assertEqual(span_list[0].attributes, expected_attrs)
|
||||||
|
self.assertEqual(
|
||||||
|
span_list[0].status.status_code, trace.StatusCode.ERROR
|
||||||
|
)
|
||||||
|
self.assertIn(
|
||||||
|
"HTTPInternalServerError", span_list[0].status.description
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
span_list[0]
|
||||||
|
.events[0]
|
||||||
|
.attributes[exception_attributes.EXCEPTION_TYPE],
|
||||||
|
"pyramid.httpexceptions.HTTPInternalServerError",
|
||||||
|
)
|
||||||
|
|
||||||
def test_internal_exception(self):
|
def test_internal_exception(self):
|
||||||
expected_attrs = expected_attributes(
|
expected_attrs = expected_attributes(
|
||||||
@ -184,6 +198,21 @@ class TestProgrammatic(InstrumentationTest, WsgiTestBase):
|
|||||||
self.assertEqual(span_list[0].name, "/hello/{helloid}")
|
self.assertEqual(span_list[0].name, "/hello/{helloid}")
|
||||||
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
|
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
|
||||||
self.assertEqual(span_list[0].attributes, expected_attrs)
|
self.assertEqual(span_list[0].attributes, expected_attrs)
|
||||||
|
self.assertEqual(
|
||||||
|
span_list[0].status.status_code, trace.StatusCode.ERROR
|
||||||
|
)
|
||||||
|
self.assertEqual(span_list[0].status.description, "error message")
|
||||||
|
|
||||||
|
expected_error_event_attrs = {
|
||||||
|
exception_attributes.EXCEPTION_TYPE: "NotImplementedError",
|
||||||
|
exception_attributes.EXCEPTION_MESSAGE: "error message",
|
||||||
|
}
|
||||||
|
self.assertEqual(span_list[0].events[0].name, "exception")
|
||||||
|
# Ensure exception event has specific attributes, but allow additional ones
|
||||||
|
self.assertLess(
|
||||||
|
expected_error_event_attrs.items(),
|
||||||
|
dict(span_list[0].events[0].attributes).items(),
|
||||||
|
)
|
||||||
|
|
||||||
def test_tween_list(self):
|
def test_tween_list(self):
|
||||||
tween_list = "opentelemetry.instrumentation.pyramid.trace_tween_factory\npyramid.tweens.excview_tween_factory"
|
tween_list = "opentelemetry.instrumentation.pyramid.trace_tween_factory\npyramid.tweens.excview_tween_factory"
|
||||||
|
Reference in New Issue
Block a user