Skip to content

Commit 33bcb6b

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

File tree

1 file changed

+45
-4
lines changed

1 file changed

+45
-4
lines changed

sentry_sdk/integrations/otlp.py

Lines changed: 45 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,28 @@ 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():
86+
# type: () -> None
87+
"""
88+
Intercept otel's Span.record_exception to automatically capture those exceptions in Sentry.
89+
"""
90+
_original_record_exception = Span.record_exception
91+
92+
def _sentry_patched_record_exception(self, exception, *args, **kwargs):
93+
# type: (Span, BaseException, *Any, **Any) -> None
94+
_original_record_exception(self, exception, *args, **kwargs)
95+
96+
event, hint = event_from_exception(
97+
exception,
98+
client_options=get_client().options,
99+
mechanism={"type": "otlp", "handled": False},
100+
)
101+
102+
capture_event(event, hint=hint)
103+
104+
Span.record_exception = _sentry_patched_record_exception
105+
106+
85107
class SentryOTLPPropagator(SentryPropagator):
86108
"""
87109
We need to override the inject of the older propagator since that
@@ -136,13 +158,28 @@ def _to_traceparent(span_context: "SpanContext") -> str:
136158

137159

138160
class OTLPIntegration(Integration):
161+
"""
162+
Automatically setup OTLP ingestion from the DSN.
163+
164+
:param setup_otlp_traces_exporter: Automatically configure an Exporter to send OTLP traces from the DSN, defaults to True.
165+
Set to False if using a custom collector or to setup the TracerProvider manually.
166+
:param setup_propagator: Automatically configure the Sentry Propagator for Distributed Tracing, defaults to True.
167+
Set to False to configure propagators manually or to disable propagation.
168+
:param capture_exceptions: Intercept and capture exceptions on the OpenTelemetry Span in Sentry as well, defaults to False.
169+
Set to True to turn on capturing but be aware that since Sentry captures most exceptions, there might be double reporting of exceptions.
170+
"""
171+
139172
identifier = "otlp"
140173

141174
def __init__(
142-
self, setup_otlp_traces_exporter: bool = True, setup_propagator: bool = True
175+
self,
176+
setup_otlp_traces_exporter: bool = True,
177+
setup_propagator: bool = True,
178+
capture_exceptions: bool = False,
143179
) -> None:
144180
self.setup_otlp_traces_exporter = setup_otlp_traces_exporter
145181
self.setup_propagator = setup_propagator
182+
self.capture_exceptions = capture_exceptions
146183

147184
@staticmethod
148185
def setup_once() -> None:
@@ -161,3 +198,7 @@ def setup_once_with_options(
161198
logger.debug("[OTLP] Setting up propagator for distributed tracing")
162199
# TODO-neel better propagator support, chain with existing ones if possible instead of replacing
163200
set_global_textmap(SentryOTLPPropagator())
201+
202+
if self.capture_exceptions:
203+
logger.debug("[OTLP] Setting up exception capturing")
204+
setup_capture_exceptions()

0 commit comments

Comments
 (0)