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,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 () -> 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+ _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+
85107class 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
138160class 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