Skip to content

Commit 53a782e

Browse files
committed
feat(otlp): Optionally capture exceptions from otel's Span.record_exception api
1 parent eedd101 commit 53a782e

File tree

1 file changed

+43
-4
lines changed

1 file changed

+43
-4
lines changed

sentry_sdk/integrations/otlp.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
from sentry_sdk import get_client
1+
from sentry_sdk import get_client, capture_event
22
from sentry_sdk.integrations import Integration, DidNotEnable
33
from sentry_sdk.scope import register_external_propagation_context
4-
from sentry_sdk.utils import logger, Dsn
4+
from sentry_sdk.utils import Dsn, logger, event_from_exception
55
from sentry_sdk.consts import VERSION, EndpointType
66
from sentry_sdk.tracing_utils import Baggage
77
from sentry_sdk.tracing import (
@@ -11,7 +11,7 @@
1111

1212
try:
1313
from opentelemetry.propagate import set_global_textmap
14-
from opentelemetry.sdk.trace import TracerProvider
14+
from opentelemetry.sdk.trace import TracerProvider, Span
1515
from opentelemetry.sdk.trace.export import BatchSpanProcessor
1616
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
1717

@@ -82,6 +82,26 @@ def setup_otlp_traces_exporter(dsn: "Optional[str]" = None) -> None:
8282
tracer_provider.add_span_processor(span_processor)
8383

8484

85+
def setup_capture_exceptions() -> None:
86+
"""
87+
Intercept otel's Span.record_exception to automatically capture those exceptions in Sentry.
88+
"""
89+
_original_record_exception = Span.record_exception
90+
91+
def _sentry_patched_record_exception(
92+
self: "Span", exception: "BaseException", *args: "Any", **kwargs: "Any"
93+
) -> None:
94+
event, hint = event_from_exception(
95+
exception,
96+
client_options=get_client().options,
97+
mechanism={"type": "otlp", "handled": False},
98+
)
99+
capture_event(event, hint=hint)
100+
_original_record_exception(self, exception, *args, **kwargs)
101+
102+
Span.record_exception = _sentry_patched_record_exception # type: ignore[method-assign]
103+
104+
85105
class SentryOTLPPropagator(SentryPropagator):
86106
"""
87107
We need to override the inject of the older propagator since that
@@ -136,13 +156,28 @@ def _to_traceparent(span_context: "SpanContext") -> str:
136156

137157

138158
class OTLPIntegration(Integration):
159+
"""
160+
Automatically setup OTLP ingestion from the DSN.
161+
162+
:param setup_otlp_traces_exporter: Automatically configure an Exporter to send OTLP traces from the DSN, defaults to True.
163+
Set to False if using a custom collector or to setup the TracerProvider manually.
164+
:param setup_propagator: Automatically configure the Sentry Propagator for Distributed Tracing, defaults to True.
165+
Set to False to configure propagators manually or to disable propagation.
166+
:param capture_exceptions: Intercept and capture exceptions on the OpenTelemetry Span in Sentry as well, defaults to False.
167+
Set to True to turn on capturing but be aware that since Sentry captures most exceptions, duplicate exceptions might be dropped by DedupeIntegration in many cases.
168+
"""
169+
139170
identifier = "otlp"
140171

141172
def __init__(
142-
self, setup_otlp_traces_exporter: bool = True, setup_propagator: bool = True
173+
self,
174+
setup_otlp_traces_exporter: bool = True,
175+
setup_propagator: bool = True,
176+
capture_exceptions: bool = False,
143177
) -> None:
144178
self.setup_otlp_traces_exporter = setup_otlp_traces_exporter
145179
self.setup_propagator = setup_propagator
180+
self.capture_exceptions = capture_exceptions
146181

147182
@staticmethod
148183
def setup_once() -> None:
@@ -161,3 +196,7 @@ def setup_once_with_options(
161196
logger.debug("[OTLP] Setting up propagator for distributed tracing")
162197
# TODO-neel better propagator support, chain with existing ones if possible instead of replacing
163198
set_global_textmap(SentryOTLPPropagator())
199+
200+
if self.capture_exceptions:
201+
logger.debug("[OTLP] Setting up exception capturing")
202+
setup_capture_exceptions()

0 commit comments

Comments
 (0)