1- from sentry_sdk import get_client
1+ from sentry_sdk import get_client , capture_event
22from sentry_sdk .integrations import Integration , DidNotEnable
33from 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
55from sentry_sdk .consts import VERSION , EndpointType
66from sentry_sdk .tracing_utils import Baggage
77from sentry_sdk .tracing import (
1111
1212try :
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 (self : "Span" , exception : "BaseException" , * args : "Any" , ** kwargs : "Any" ) -> None :
92+ _original_record_exception (self , exception , * args , ** kwargs )
93+
94+ event , hint = event_from_exception (
95+ exception ,
96+ client_options = get_client ().options ,
97+ mechanism = {"type" : "otlp" , "handled" : False },
98+ )
99+
100+ capture_event (event , hint = hint )
101+
102+ Span .record_exception = _sentry_patched_record_exception
103+
104+
85105class 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
138158class 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, there might be double reporting of exceptions.
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