Skip to content

Commit cea505e

Browse files
rbagdxrmx
authored andcommitted
Added recording of exceptions in Pyramid (open-telemetry#2622)
1 parent eccc931 commit cea505e

File tree

5 files changed

+45
-2
lines changed

5 files changed

+45
-2
lines changed

Diff for: CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12+
- `opentelemetry-instrumentation-pyramid` Record exceptions raised when serving a request
13+
([#2622](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2622))
1214
- `opentelemetry-sdk-extension-aws` Add AwsXrayLambdaPropagator
1315
([#2573](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2573))
1416

Diff for: instrumentation/opentelemetry-instrumentation-pyramid/src/opentelemetry/instrumentation/pyramid/callbacks.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
from opentelemetry.metrics import get_meter
3232
from opentelemetry.semconv.metrics import MetricInstruments
3333
from opentelemetry.semconv.trace import SpanAttributes
34+
from opentelemetry.trace.status import Status, StatusCode
3435
from opentelemetry.util.http import get_excluded_urls
3536

3637
TWEEN_NAME = "opentelemetry.instrumentation.pyramid.trace_tween_factory"
@@ -180,6 +181,7 @@ def trace_tween(request):
180181

181182
response = None
182183
status = None
184+
recordable_exc = None
183185

184186
try:
185187
response = handler(request)
@@ -190,11 +192,14 @@ def trace_tween(request):
190192
# As described in docs, Pyramid exceptions are all valid
191193
# response types
192194
response = exc
195+
if isinstance(exc, HTTPServerError):
196+
recordable_exc = exc
193197
raise
194-
except BaseException:
198+
except BaseException as exc:
195199
# In the case that a non-HTTPException is bubbled up we
196200
# should infer a internal server error and raise
197201
status = "500 InternalServerError"
202+
recordable_exc = exc
198203
raise
199204
finally:
200205
duration = max(round((default_timer() - start) * 1000), 0)
@@ -222,6 +227,12 @@ def trace_tween(request):
222227
getattr(response, "headerlist", None),
223228
)
224229

230+
if recordable_exc is not None:
231+
span.set_status(
232+
Status(StatusCode.ERROR, str(recordable_exc))
233+
)
234+
span.record_exception(recordable_exc)
235+
225236
if span.is_recording() and span.kind == trace.SpanKind.SERVER:
226237
custom_attributes = (
227238
otel_wsgi.collect_custom_response_headers_attributes(

Diff for: instrumentation/opentelemetry-instrumentation-pyramid/tests/pyramid_base_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def _hello_endpoint(request):
3535
if helloid == 204:
3636
raise exc.HTTPNoContent()
3737
if helloid == 900:
38-
raise NotImplementedError()
38+
raise NotImplementedError("error message")
3939
return Response("Hello: " + str(helloid))
4040

4141
@staticmethod

Diff for: instrumentation/opentelemetry-instrumentation-pyramid/tests/test_automatic.py

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ def test_redirect_response_is_not_an_error(self):
121121
span_list = self.memory_exporter.get_finished_spans()
122122
self.assertEqual(len(span_list), 1)
123123
self.assertEqual(span_list[0].status.status_code, StatusCode.UNSET)
124+
self.assertEqual(len(span_list[0].events), 0)
124125

125126
PyramidInstrumentor().uninstrument()
126127

Diff for: instrumentation/opentelemetry-instrumentation-pyramid/tests/test_programmatic.py

+29
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
set_global_response_propagator,
2424
)
2525
from opentelemetry.instrumentation.pyramid import PyramidInstrumentor
26+
from opentelemetry.semconv.attributes import exception_attributes
2627
from opentelemetry.semconv.trace import SpanAttributes
2728
from opentelemetry.test.wsgitestutil import WsgiTestBase
2829
from opentelemetry.util.http import get_excluded_urls
@@ -149,6 +150,7 @@ def test_404(self):
149150
self.assertEqual(span_list[0].name, "POST /bye")
150151
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
151152
self.assertEqual(span_list[0].attributes, expected_attrs)
153+
self.assertEqual(len(span_list[0].events), 0)
152154

153155
def test_internal_error(self):
154156
expected_attrs = expected_attributes(
@@ -166,6 +168,18 @@ def test_internal_error(self):
166168
self.assertEqual(span_list[0].name, "/hello/{helloid}")
167169
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
168170
self.assertEqual(span_list[0].attributes, expected_attrs)
171+
self.assertEqual(
172+
span_list[0].status.status_code, trace.StatusCode.ERROR
173+
)
174+
self.assertIn(
175+
"HTTPInternalServerError", span_list[0].status.description
176+
)
177+
self.assertEqual(
178+
span_list[0]
179+
.events[0]
180+
.attributes[exception_attributes.EXCEPTION_TYPE],
181+
"pyramid.httpexceptions.HTTPInternalServerError",
182+
)
169183

170184
def test_internal_exception(self):
171185
expected_attrs = expected_attributes(
@@ -184,6 +198,21 @@ def test_internal_exception(self):
184198
self.assertEqual(span_list[0].name, "/hello/{helloid}")
185199
self.assertEqual(span_list[0].kind, trace.SpanKind.SERVER)
186200
self.assertEqual(span_list[0].attributes, expected_attrs)
201+
self.assertEqual(
202+
span_list[0].status.status_code, trace.StatusCode.ERROR
203+
)
204+
self.assertEqual(span_list[0].status.description, "error message")
205+
206+
expected_error_event_attrs = {
207+
exception_attributes.EXCEPTION_TYPE: "NotImplementedError",
208+
exception_attributes.EXCEPTION_MESSAGE: "error message",
209+
}
210+
self.assertEqual(span_list[0].events[0].name, "exception")
211+
# Ensure exception event has specific attributes, but allow additional ones
212+
self.assertLess(
213+
expected_error_event_attrs.items(),
214+
dict(span_list[0].events[0].attributes).items(),
215+
)
187216

188217
def test_tween_list(self):
189218
tween_list = "opentelemetry.instrumentation.pyramid.trace_tween_factory\npyramid.tweens.excview_tween_factory"

0 commit comments

Comments
 (0)