We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Empty DSN crashes Otel integration
### How do you use Sentry?

Sentry Saas (sentry.io)

### Version

1.12.0

### Steps to Reproduce

Do not give a DSN when initializing the SDK with the Otel integration. 

You will get this:

```
recommendation-service   | 2023-01-18 10:24:50,723 ERROR [grpc._server] [_server.py:454] [trace_id=0 span_id=0 resource.service.name=recommendationservice] - Exce
ption calling application: Unsupported scheme ''
recommendation-service   | Traceback (most recent call last):
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/grpc/_server.py", line 444, in _call_behavior
recommendation-service   |     response_or_iterator = behavior(argument, context)
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/opentelemetry/instrumentation/grpc/_server.py", line 282, in telemetry_interceptor
recommendation-service   |     with self._start_span(
recommendation-service   |   File "/usr/local/lib/python3.10/contextlib.py", line 135, in __enter__
recommendation-service   |     return next(self.gen)
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py", line 1017, in start_as_current_span
recommendation-service   |     span = self.start_span(
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py", line 1107, in start_span
recommendation-service   |     span.start(start_time=start_time, parent_context=context)
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py", line 870, in start
recommendation-service   |     self._span_processor.on_start(self, parent_context=parent_context)
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py", line 162, in on_start
recommendation-service   |     sp.on_start(span, parent_context=parent_context)
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/sentry_sdk/integrations/opentelemetry/span_processor.py", line 107, in on_start
recommendation-service   |     if self._is_sentry_span(hub, otel_span):
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/sentry_sdk/integrations/opentelemetry/span_processor.py", line 177, in _is_sentry_span
recommendation-service   |     dsn_url = hub.client and Dsn(hub.client.dsn or "").netloc
recommendation-service   |   File "/usr/local/lib/python3.10/site-packages/sentry_sdk/utils.py", line 200, in __init__
recommendation-service   |     raise BadDsn("Unsupported scheme %r" % parts.scheme)
recommendation-service   | sentry_sdk.utils.BadDsn: Unsupported scheme ''
```

### Expected Result

The Sentry SDK should just give a Warning and then do nothing and NOT crash everything.

### Actual Result

Crash

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sentry_sdk/integrations/opentelemetry/span_processor.py`
Content:
```
1 from datetime import datetime
2 
3 from opentelemetry.context import get_value  # type: ignore
4 from opentelemetry.sdk.trace import SpanProcessor  # type: ignore
5 from opentelemetry.semconv.trace import SpanAttributes  # type: ignore
6 from opentelemetry.trace import (  # type: ignore
7     format_span_id,
8     format_trace_id,
9     get_current_span,
10     SpanContext,
11     Span as OTelSpan,
12     SpanKind,
13 )
14 from opentelemetry.trace.span import (  # type: ignore
15     INVALID_SPAN_ID,
16     INVALID_TRACE_ID,
17 )
18 from sentry_sdk.consts import INSTRUMENTER
19 from sentry_sdk.hub import Hub
20 from sentry_sdk.integrations.opentelemetry.consts import (
21     SENTRY_BAGGAGE_KEY,
22     SENTRY_TRACE_KEY,
23 )
24 from sentry_sdk.scope import add_global_event_processor
25 from sentry_sdk.tracing import Transaction, Span as SentrySpan
26 from sentry_sdk.utils import Dsn
27 from sentry_sdk._types import MYPY
28 
29 from urllib3.util import parse_url as urlparse  # type: ignore
30 
31 if MYPY:
32     from typing import Any
33     from typing import Dict
34     from typing import Union
35     from sentry_sdk._types import Event, Hint
36 
37 OPEN_TELEMETRY_CONTEXT = "otel"
38 
39 
40 def link_trace_context_to_error_event(event, otel_span_map):
41     # type: (Event, Dict[str, Union[Transaction, OTelSpan]]) -> Event
42     hub = Hub.current
43     if not hub:
44         return event
45 
46     if hub.client and hub.client.options["instrumenter"] != INSTRUMENTER.OTEL:
47         return event
48 
49     if hasattr(event, "type") and event["type"] == "transaction":
50         return event
51 
52     otel_span = get_current_span()
53     if not otel_span:
54         return event
55 
56     ctx = otel_span.get_span_context()
57     trace_id = format_trace_id(ctx.trace_id)
58     span_id = format_span_id(ctx.span_id)
59 
60     if trace_id == INVALID_TRACE_ID or span_id == INVALID_SPAN_ID:
61         return event
62 
63     sentry_span = otel_span_map.get(span_id, None)
64     if not sentry_span:
65         return event
66 
67     contexts = event.setdefault("contexts", {})
68     contexts.setdefault("trace", {}).update(sentry_span.get_trace_context())
69 
70     return event
71 
72 
73 class SentrySpanProcessor(SpanProcessor):  # type: ignore
74     """
75     Converts OTel spans into Sentry spans so they can be sent to the Sentry backend.
76     """
77 
78     # The mapping from otel span ids to sentry spans
79     otel_span_map = {}  # type: Dict[str, Union[Transaction, OTelSpan]]
80 
81     def __new__(cls):
82         # type: () -> SentrySpanProcessor
83         if not hasattr(cls, "instance"):
84             cls.instance = super(SentrySpanProcessor, cls).__new__(cls)
85 
86         return cls.instance
87 
88     def __init__(self):
89         # type: () -> None
90         @add_global_event_processor
91         def global_event_processor(event, hint):
92             # type: (Event, Hint) -> Event
93             return link_trace_context_to_error_event(event, self.otel_span_map)
94 
95     def on_start(self, otel_span, parent_context=None):
96         # type: (OTelSpan, SpanContext) -> None
97         hub = Hub.current
98         if not hub:
99             return
100 
101         if hub.client and hub.client.options["instrumenter"] != INSTRUMENTER.OTEL:
102             return
103 
104         if not otel_span.context.is_valid:
105             return
106 
107         if self._is_sentry_span(hub, otel_span):
108             return
109 
110         trace_data = self._get_trace_data(otel_span, parent_context)
111 
112         parent_span_id = trace_data["parent_span_id"]
113         sentry_parent_span = (
114             self.otel_span_map.get(parent_span_id, None) if parent_span_id else None
115         )
116 
117         sentry_span = None
118         if sentry_parent_span:
119             sentry_span = sentry_parent_span.start_child(
120                 span_id=trace_data["span_id"],
121                 description=otel_span.name,
122                 start_timestamp=datetime.fromtimestamp(otel_span.start_time / 1e9),
123                 instrumenter=INSTRUMENTER.OTEL,
124             )
125         else:
126             sentry_span = hub.start_transaction(
127                 name=otel_span.name,
128                 span_id=trace_data["span_id"],
129                 parent_span_id=parent_span_id,
130                 trace_id=trace_data["trace_id"],
131                 baggage=trace_data["baggage"],
132                 start_timestamp=datetime.fromtimestamp(otel_span.start_time / 1e9),
133                 instrumenter=INSTRUMENTER.OTEL,
134             )
135 
136         self.otel_span_map[trace_data["span_id"]] = sentry_span
137 
138     def on_end(self, otel_span):
139         # type: (OTelSpan) -> None
140         hub = Hub.current
141         if not hub:
142             return
143 
144         if hub.client and hub.client.options["instrumenter"] != INSTRUMENTER.OTEL:
145             return
146 
147         if not otel_span.context.is_valid:
148             return
149 
150         span_id = format_span_id(otel_span.context.span_id)
151         sentry_span = self.otel_span_map.pop(span_id, None)
152         if not sentry_span:
153             return
154 
155         sentry_span.op = otel_span.name
156 
157         if isinstance(sentry_span, Transaction):
158             sentry_span.name = otel_span.name
159             sentry_span.set_context(
160                 OPEN_TELEMETRY_CONTEXT, self._get_otel_context(otel_span)
161             )
162 
163         else:
164             self._update_span_with_otel_data(sentry_span, otel_span)
165 
166         sentry_span.finish(
167             end_timestamp=datetime.fromtimestamp(otel_span.end_time / 1e9)
168         )
169 
170     def _is_sentry_span(self, hub, otel_span):
171         # type: (Hub, OTelSpan) -> bool
172         """
173         Break infinite loop:
174         HTTP requests to Sentry are caught by OTel and send again to Sentry.
175         """
176         otel_span_url = otel_span.attributes.get(SpanAttributes.HTTP_URL, None)
177         dsn_url = hub.client and Dsn(hub.client.dsn or "").netloc
178 
179         if otel_span_url and dsn_url in otel_span_url:
180             return True
181 
182         return False
183 
184     def _get_otel_context(self, otel_span):
185         # type: (OTelSpan) -> Dict[str, Any]
186         """
187         Returns the OTel context for Sentry.
188         See: https://develop.sentry.dev/sdk/performance/opentelemetry/#step-5-add-opentelemetry-context
189         """
190         ctx = {}
191 
192         if otel_span.attributes:
193             ctx["attributes"] = dict(otel_span.attributes)
194 
195         if otel_span.resource.attributes:
196             ctx["resource"] = dict(otel_span.resource.attributes)
197 
198         return ctx
199 
200     def _get_trace_data(self, otel_span, parent_context):
201         # type: (OTelSpan, SpanContext) -> Dict[str, Any]
202         """
203         Extracts tracing information from one OTel span and its parent OTel context.
204         """
205         trace_data = {}
206 
207         span_id = format_span_id(otel_span.context.span_id)
208         trace_data["span_id"] = span_id
209 
210         trace_id = format_trace_id(otel_span.context.trace_id)
211         trace_data["trace_id"] = trace_id
212 
213         parent_span_id = (
214             format_span_id(otel_span.parent.span_id) if otel_span.parent else None
215         )
216         trace_data["parent_span_id"] = parent_span_id
217 
218         sentry_trace_data = get_value(SENTRY_TRACE_KEY, parent_context)
219         trace_data["parent_sampled"] = (
220             sentry_trace_data["parent_sampled"] if sentry_trace_data else None
221         )
222 
223         baggage = get_value(SENTRY_BAGGAGE_KEY, parent_context)
224         trace_data["baggage"] = baggage
225 
226         return trace_data
227 
228     def _update_span_with_otel_data(self, sentry_span, otel_span):
229         # type: (SentrySpan, OTelSpan) -> None
230         """
231         Convert OTel span data and update the Sentry span with it.
232         This should eventually happen on the server when ingesting the spans.
233         """
234         for key, val in otel_span.attributes.items():
235             sentry_span.set_data(key, val)
236 
237         sentry_span.set_data("otel.kind", otel_span.kind)
238 
239         op = otel_span.name
240         description = otel_span.name
241 
242         http_method = otel_span.attributes.get(SpanAttributes.HTTP_METHOD, None)
243         db_query = otel_span.attributes.get(SpanAttributes.DB_SYSTEM, None)
244 
245         if http_method:
246             op = "http"
247 
248             if otel_span.kind == SpanKind.SERVER:
249                 op += ".server"
250             elif otel_span.kind == SpanKind.CLIENT:
251                 op += ".client"
252 
253             description = http_method
254 
255             peer_name = otel_span.attributes.get(SpanAttributes.NET_PEER_NAME, None)
256             if peer_name:
257                 description += " {}".format(peer_name)
258 
259             target = otel_span.attributes.get(SpanAttributes.HTTP_TARGET, None)
260             if target:
261                 description += " {}".format(target)
262 
263             if not peer_name and not target:
264                 url = otel_span.attributes.get(SpanAttributes.HTTP_URL, None)
265                 if url:
266                     parsed_url = urlparse(url)
267                     url = f"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}"
268                     description += " {}".format(url)
269 
270             status_code = otel_span.attributes.get(
271                 SpanAttributes.HTTP_STATUS_CODE, None
272             )
273             if status_code:
274                 sentry_span.set_http_status(status_code)
275 
276         elif db_query:
277             op = "db"
278             statement = otel_span.attributes.get(SpanAttributes.DB_STATEMENT, None)
279             if statement:
280                 description = statement
281 
282         sentry_span.op = op
283         sentry_span.description = description
284
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sentry_sdk/integrations/opentelemetry/span_processor.py b/sentry_sdk/integrations/opentelemetry/span_processor.py
--- a/sentry_sdk/integrations/opentelemetry/span_processor.py
+++ b/sentry_sdk/integrations/opentelemetry/span_processor.py
@@ -98,6 +98,14 @@
         if not hub:
             return
 
+        if not hub.client or (hub.client and not hub.client.dsn):
+            return
+
+        try:
+            _ = Dsn(hub.client.dsn or "")
+        except Exception:
+            return
+
         if hub.client and hub.client.options["instrumenter"] != INSTRUMENTER.OTEL:
             return

{"golden_diff": "diff --git a/sentry_sdk/integrations/opentelemetry/span_processor.py b/sentry_sdk/integrations/opentelemetry/span_processor.py\n--- a/sentry_sdk/integrations/opentelemetry/span_processor.py\n+++ b/sentry_sdk/integrations/opentelemetry/span_processor.py\n@@ -98,6 +98,14 @@\n         if not hub:\n             return\n \n+        if not hub.client or (hub.client and not hub.client.dsn):\n+            return\n+\n+        try:\n+            _ = Dsn(hub.client.dsn or \"\")\n+        except Exception:\n+            return\n+\n         if hub.client and hub.client.options[\"instrumenter\"] != INSTRUMENTER.OTEL:\n             return\n", "issue": "Empty DSN crashes Otel integration\n### How do you use Sentry?\n\nSentry Saas (sentry.io)\n\n### Version\n\n1.12.0\n\n### Steps to Reproduce\n\nDo not give a DSN when initializing the SDK with the Otel integration. \r\n\r\nYou will get this:\r\n\r\n```\r\nrecommendation-service   | 2023-01-18 10:24:50,723 ERROR [grpc._server] [_server.py:454] [trace_id=0 span_id=0 resource.service.name=recommendationservice] - Exce\r\nption calling application: Unsupported scheme ''\r\nrecommendation-service   | Traceback (most recent call last):\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/grpc/_server.py\", line 444, in _call_behavior\r\nrecommendation-service   |     response_or_iterator = behavior(argument, context)\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/opentelemetry/instrumentation/grpc/_server.py\", line 282, in telemetry_interceptor\r\nrecommendation-service   |     with self._start_span(\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/contextlib.py\", line 135, in __enter__\r\nrecommendation-service   |     return next(self.gen)\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py\", line 1017, in start_as_current_span\r\nrecommendation-service   |     span = self.start_span(\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py\", line 1107, in start_span\r\nrecommendation-service   |     span.start(start_time=start_time, parent_context=context)\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py\", line 870, in start\r\nrecommendation-service   |     self._span_processor.on_start(self, parent_context=parent_context)\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/opentelemetry/sdk/trace/__init__.py\", line 162, in on_start\r\nrecommendation-service   |     sp.on_start(span, parent_context=parent_context)\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/sentry_sdk/integrations/opentelemetry/span_processor.py\", line 107, in on_start\r\nrecommendation-service   |     if self._is_sentry_span(hub, otel_span):\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/sentry_sdk/integrations/opentelemetry/span_processor.py\", line 177, in _is_sentry_span\r\nrecommendation-service   |     dsn_url = hub.client and Dsn(hub.client.dsn or \"\").netloc\r\nrecommendation-service   |   File \"/usr/local/lib/python3.10/site-packages/sentry_sdk/utils.py\", line 200, in __init__\r\nrecommendation-service   |     raise BadDsn(\"Unsupported scheme %r\" % parts.scheme)\r\nrecommendation-service   | sentry_sdk.utils.BadDsn: Unsupported scheme ''\r\n```\n\n### Expected Result\n\nThe Sentry SDK should just give a Warning and then do nothing and NOT crash everything.\n\n### Actual Result\n\nCrash\n", "before_files": [{"content": "from datetime import datetime\n\nfrom opentelemetry.context import get_value  # type: ignore\nfrom opentelemetry.sdk.trace import SpanProcessor  # type: ignore\nfrom opentelemetry.semconv.trace import SpanAttributes  # type: ignore\nfrom opentelemetry.trace import (  # type: ignore\n    format_span_id,\n    format_trace_id,\n    get_current_span,\n    SpanContext,\n    Span as OTelSpan,\n    SpanKind,\n)\nfrom opentelemetry.trace.span import (  # type: ignore\n    INVALID_SPAN_ID,\n    INVALID_TRACE_ID,\n)\nfrom sentry_sdk.consts import INSTRUMENTER\nfrom sentry_sdk.hub import Hub\nfrom sentry_sdk.integrations.opentelemetry.consts import (\n    SENTRY_BAGGAGE_KEY,\n    SENTRY_TRACE_KEY,\n)\nfrom sentry_sdk.scope import add_global_event_processor\nfrom sentry_sdk.tracing import Transaction, Span as SentrySpan\nfrom sentry_sdk.utils import Dsn\nfrom sentry_sdk._types import MYPY\n\nfrom urllib3.util import parse_url as urlparse  # type: ignore\n\nif MYPY:\n    from typing import Any\n    from typing import Dict\n    from typing import Union\n    from sentry_sdk._types import Event, Hint\n\nOPEN_TELEMETRY_CONTEXT = \"otel\"\n\n\ndef link_trace_context_to_error_event(event, otel_span_map):\n    # type: (Event, Dict[str, Union[Transaction, OTelSpan]]) -> Event\n    hub = Hub.current\n    if not hub:\n        return event\n\n    if hub.client and hub.client.options[\"instrumenter\"] != INSTRUMENTER.OTEL:\n        return event\n\n    if hasattr(event, \"type\") and event[\"type\"] == \"transaction\":\n        return event\n\n    otel_span = get_current_span()\n    if not otel_span:\n        return event\n\n    ctx = otel_span.get_span_context()\n    trace_id = format_trace_id(ctx.trace_id)\n    span_id = format_span_id(ctx.span_id)\n\n    if trace_id == INVALID_TRACE_ID or span_id == INVALID_SPAN_ID:\n        return event\n\n    sentry_span = otel_span_map.get(span_id, None)\n    if not sentry_span:\n        return event\n\n    contexts = event.setdefault(\"contexts\", {})\n    contexts.setdefault(\"trace\", {}).update(sentry_span.get_trace_context())\n\n    return event\n\n\nclass SentrySpanProcessor(SpanProcessor):  # type: ignore\n    \"\"\"\n    Converts OTel spans into Sentry spans so they can be sent to the Sentry backend.\n    \"\"\"\n\n    # The mapping from otel span ids to sentry spans\n    otel_span_map = {}  # type: Dict[str, Union[Transaction, OTelSpan]]\n\n    def __new__(cls):\n        # type: () -> SentrySpanProcessor\n        if not hasattr(cls, \"instance\"):\n            cls.instance = super(SentrySpanProcessor, cls).__new__(cls)\n\n        return cls.instance\n\n    def __init__(self):\n        # type: () -> None\n        @add_global_event_processor\n        def global_event_processor(event, hint):\n            # type: (Event, Hint) -> Event\n            return link_trace_context_to_error_event(event, self.otel_span_map)\n\n    def on_start(self, otel_span, parent_context=None):\n        # type: (OTelSpan, SpanContext) -> None\n        hub = Hub.current\n        if not hub:\n            return\n\n        if hub.client and hub.client.options[\"instrumenter\"] != INSTRUMENTER.OTEL:\n            return\n\n        if not otel_span.context.is_valid:\n            return\n\n        if self._is_sentry_span(hub, otel_span):\n            return\n\n        trace_data = self._get_trace_data(otel_span, parent_context)\n\n        parent_span_id = trace_data[\"parent_span_id\"]\n        sentry_parent_span = (\n            self.otel_span_map.get(parent_span_id, None) if parent_span_id else None\n        )\n\n        sentry_span = None\n        if sentry_parent_span:\n            sentry_span = sentry_parent_span.start_child(\n                span_id=trace_data[\"span_id\"],\n                description=otel_span.name,\n                start_timestamp=datetime.fromtimestamp(otel_span.start_time / 1e9),\n                instrumenter=INSTRUMENTER.OTEL,\n            )\n        else:\n            sentry_span = hub.start_transaction(\n                name=otel_span.name,\n                span_id=trace_data[\"span_id\"],\n                parent_span_id=parent_span_id,\n                trace_id=trace_data[\"trace_id\"],\n                baggage=trace_data[\"baggage\"],\n                start_timestamp=datetime.fromtimestamp(otel_span.start_time / 1e9),\n                instrumenter=INSTRUMENTER.OTEL,\n            )\n\n        self.otel_span_map[trace_data[\"span_id\"]] = sentry_span\n\n    def on_end(self, otel_span):\n        # type: (OTelSpan) -> None\n        hub = Hub.current\n        if not hub:\n            return\n\n        if hub.client and hub.client.options[\"instrumenter\"] != INSTRUMENTER.OTEL:\n            return\n\n        if not otel_span.context.is_valid:\n            return\n\n        span_id = format_span_id(otel_span.context.span_id)\n        sentry_span = self.otel_span_map.pop(span_id, None)\n        if not sentry_span:\n            return\n\n        sentry_span.op = otel_span.name\n\n        if isinstance(sentry_span, Transaction):\n            sentry_span.name = otel_span.name\n            sentry_span.set_context(\n                OPEN_TELEMETRY_CONTEXT, self._get_otel_context(otel_span)\n            )\n\n        else:\n            self._update_span_with_otel_data(sentry_span, otel_span)\n\n        sentry_span.finish(\n            end_timestamp=datetime.fromtimestamp(otel_span.end_time / 1e9)\n        )\n\n    def _is_sentry_span(self, hub, otel_span):\n        # type: (Hub, OTelSpan) -> bool\n        \"\"\"\n        Break infinite loop:\n        HTTP requests to Sentry are caught by OTel and send again to Sentry.\n        \"\"\"\n        otel_span_url = otel_span.attributes.get(SpanAttributes.HTTP_URL, None)\n        dsn_url = hub.client and Dsn(hub.client.dsn or \"\").netloc\n\n        if otel_span_url and dsn_url in otel_span_url:\n            return True\n\n        return False\n\n    def _get_otel_context(self, otel_span):\n        # type: (OTelSpan) -> Dict[str, Any]\n        \"\"\"\n        Returns the OTel context for Sentry.\n        See: https://develop.sentry.dev/sdk/performance/opentelemetry/#step-5-add-opentelemetry-context\n        \"\"\"\n        ctx = {}\n\n        if otel_span.attributes:\n            ctx[\"attributes\"] = dict(otel_span.attributes)\n\n        if otel_span.resource.attributes:\n            ctx[\"resource\"] = dict(otel_span.resource.attributes)\n\n        return ctx\n\n    def _get_trace_data(self, otel_span, parent_context):\n        # type: (OTelSpan, SpanContext) -> Dict[str, Any]\n        \"\"\"\n        Extracts tracing information from one OTel span and its parent OTel context.\n        \"\"\"\n        trace_data = {}\n\n        span_id = format_span_id(otel_span.context.span_id)\n        trace_data[\"span_id\"] = span_id\n\n        trace_id = format_trace_id(otel_span.context.trace_id)\n        trace_data[\"trace_id\"] = trace_id\n\n        parent_span_id = (\n            format_span_id(otel_span.parent.span_id) if otel_span.parent else None\n        )\n        trace_data[\"parent_span_id\"] = parent_span_id\n\n        sentry_trace_data = get_value(SENTRY_TRACE_KEY, parent_context)\n        trace_data[\"parent_sampled\"] = (\n            sentry_trace_data[\"parent_sampled\"] if sentry_trace_data else None\n        )\n\n        baggage = get_value(SENTRY_BAGGAGE_KEY, parent_context)\n        trace_data[\"baggage\"] = baggage\n\n        return trace_data\n\n    def _update_span_with_otel_data(self, sentry_span, otel_span):\n        # type: (SentrySpan, OTelSpan) -> None\n        \"\"\"\n        Convert OTel span data and update the Sentry span with it.\n        This should eventually happen on the server when ingesting the spans.\n        \"\"\"\n        for key, val in otel_span.attributes.items():\n            sentry_span.set_data(key, val)\n\n        sentry_span.set_data(\"otel.kind\", otel_span.kind)\n\n        op = otel_span.name\n        description = otel_span.name\n\n        http_method = otel_span.attributes.get(SpanAttributes.HTTP_METHOD, None)\n        db_query = otel_span.attributes.get(SpanAttributes.DB_SYSTEM, None)\n\n        if http_method:\n            op = \"http\"\n\n            if otel_span.kind == SpanKind.SERVER:\n                op += \".server\"\n            elif otel_span.kind == SpanKind.CLIENT:\n                op += \".client\"\n\n            description = http_method\n\n            peer_name = otel_span.attributes.get(SpanAttributes.NET_PEER_NAME, None)\n            if peer_name:\n                description += \" {}\".format(peer_name)\n\n            target = otel_span.attributes.get(SpanAttributes.HTTP_TARGET, None)\n            if target:\n                description += \" {}\".format(target)\n\n            if not peer_name and not target:\n                url = otel_span.attributes.get(SpanAttributes.HTTP_URL, None)\n                if url:\n                    parsed_url = urlparse(url)\n                    url = f\"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}\"\n                    description += \" {}\".format(url)\n\n            status_code = otel_span.attributes.get(\n                SpanAttributes.HTTP_STATUS_CODE, None\n            )\n            if status_code:\n                sentry_span.set_http_status(status_code)\n\n        elif db_query:\n            op = \"db\"\n            statement = otel_span.attributes.get(SpanAttributes.DB_STATEMENT, None)\n            if statement:\n                description = statement\n\n        sentry_span.op = op\n        sentry_span.description = description\n", "path": "sentry_sdk/integrations/opentelemetry/span_processor.py"}], "after_files": [{"content": "from datetime import datetime\n\nfrom opentelemetry.context import get_value  # type: ignore\nfrom opentelemetry.sdk.trace import SpanProcessor  # type: ignore\nfrom opentelemetry.semconv.trace import SpanAttributes  # type: ignore\nfrom opentelemetry.trace import (  # type: ignore\n    format_span_id,\n    format_trace_id,\n    get_current_span,\n    SpanContext,\n    Span as OTelSpan,\n    SpanKind,\n)\nfrom opentelemetry.trace.span import (  # type: ignore\n    INVALID_SPAN_ID,\n    INVALID_TRACE_ID,\n)\nfrom sentry_sdk.consts import INSTRUMENTER\nfrom sentry_sdk.hub import Hub\nfrom sentry_sdk.integrations.opentelemetry.consts import (\n    SENTRY_BAGGAGE_KEY,\n    SENTRY_TRACE_KEY,\n)\nfrom sentry_sdk.scope import add_global_event_processor\nfrom sentry_sdk.tracing import Transaction, Span as SentrySpan\nfrom sentry_sdk.utils import Dsn\nfrom sentry_sdk._types import MYPY\n\nfrom urllib3.util import parse_url as urlparse  # type: ignore\n\nif MYPY:\n    from typing import Any\n    from typing import Dict\n    from typing import Union\n    from sentry_sdk._types import Event, Hint\n\nOPEN_TELEMETRY_CONTEXT = \"otel\"\n\n\ndef link_trace_context_to_error_event(event, otel_span_map):\n    # type: (Event, Dict[str, Union[Transaction, OTelSpan]]) -> Event\n    hub = Hub.current\n    if not hub:\n        return event\n\n    if hub.client and hub.client.options[\"instrumenter\"] != INSTRUMENTER.OTEL:\n        return event\n\n    if hasattr(event, \"type\") and event[\"type\"] == \"transaction\":\n        return event\n\n    otel_span = get_current_span()\n    if not otel_span:\n        return event\n\n    ctx = otel_span.get_span_context()\n    trace_id = format_trace_id(ctx.trace_id)\n    span_id = format_span_id(ctx.span_id)\n\n    if trace_id == INVALID_TRACE_ID or span_id == INVALID_SPAN_ID:\n        return event\n\n    sentry_span = otel_span_map.get(span_id, None)\n    if not sentry_span:\n        return event\n\n    contexts = event.setdefault(\"contexts\", {})\n    contexts.setdefault(\"trace\", {}).update(sentry_span.get_trace_context())\n\n    return event\n\n\nclass SentrySpanProcessor(SpanProcessor):  # type: ignore\n    \"\"\"\n    Converts OTel spans into Sentry spans so they can be sent to the Sentry backend.\n    \"\"\"\n\n    # The mapping from otel span ids to sentry spans\n    otel_span_map = {}  # type: Dict[str, Union[Transaction, OTelSpan]]\n\n    def __new__(cls):\n        # type: () -> SentrySpanProcessor\n        if not hasattr(cls, \"instance\"):\n            cls.instance = super(SentrySpanProcessor, cls).__new__(cls)\n\n        return cls.instance\n\n    def __init__(self):\n        # type: () -> None\n        @add_global_event_processor\n        def global_event_processor(event, hint):\n            # type: (Event, Hint) -> Event\n            return link_trace_context_to_error_event(event, self.otel_span_map)\n\n    def on_start(self, otel_span, parent_context=None):\n        # type: (OTelSpan, SpanContext) -> None\n        hub = Hub.current\n        if not hub:\n            return\n\n        if not hub.client or (hub.client and not hub.client.dsn):\n            return\n\n        try:\n            _ = Dsn(hub.client.dsn or \"\")\n        except Exception:\n            return\n\n        if hub.client and hub.client.options[\"instrumenter\"] != INSTRUMENTER.OTEL:\n            return\n\n        if not otel_span.context.is_valid:\n            return\n\n        if self._is_sentry_span(hub, otel_span):\n            return\n\n        trace_data = self._get_trace_data(otel_span, parent_context)\n\n        parent_span_id = trace_data[\"parent_span_id\"]\n        sentry_parent_span = (\n            self.otel_span_map.get(parent_span_id, None) if parent_span_id else None\n        )\n\n        sentry_span = None\n        if sentry_parent_span:\n            sentry_span = sentry_parent_span.start_child(\n                span_id=trace_data[\"span_id\"],\n                description=otel_span.name,\n                start_timestamp=datetime.fromtimestamp(otel_span.start_time / 1e9),\n                instrumenter=INSTRUMENTER.OTEL,\n            )\n        else:\n            sentry_span = hub.start_transaction(\n                name=otel_span.name,\n                span_id=trace_data[\"span_id\"],\n                parent_span_id=parent_span_id,\n                trace_id=trace_data[\"trace_id\"],\n                baggage=trace_data[\"baggage\"],\n                start_timestamp=datetime.fromtimestamp(otel_span.start_time / 1e9),\n                instrumenter=INSTRUMENTER.OTEL,\n            )\n\n        self.otel_span_map[trace_data[\"span_id\"]] = sentry_span\n\n    def on_end(self, otel_span):\n        # type: (OTelSpan) -> None\n        hub = Hub.current\n        if not hub:\n            return\n\n        if hub.client and hub.client.options[\"instrumenter\"] != INSTRUMENTER.OTEL:\n            return\n\n        if not otel_span.context.is_valid:\n            return\n\n        span_id = format_span_id(otel_span.context.span_id)\n        sentry_span = self.otel_span_map.pop(span_id, None)\n        if not sentry_span:\n            return\n\n        sentry_span.op = otel_span.name\n\n        if isinstance(sentry_span, Transaction):\n            sentry_span.name = otel_span.name\n            sentry_span.set_context(\n                OPEN_TELEMETRY_CONTEXT, self._get_otel_context(otel_span)\n            )\n\n        else:\n            self._update_span_with_otel_data(sentry_span, otel_span)\n\n        sentry_span.finish(\n            end_timestamp=datetime.fromtimestamp(otel_span.end_time / 1e9)\n        )\n\n    def _is_sentry_span(self, hub, otel_span):\n        # type: (Hub, OTelSpan) -> bool\n        \"\"\"\n        Break infinite loop:\n        HTTP requests to Sentry are caught by OTel and send again to Sentry.\n        \"\"\"\n        otel_span_url = otel_span.attributes.get(SpanAttributes.HTTP_URL, None)\n        dsn_url = hub.client and Dsn(hub.client.dsn or \"\").netloc\n\n        if otel_span_url and dsn_url in otel_span_url:\n            return True\n\n        return False\n\n    def _get_otel_context(self, otel_span):\n        # type: (OTelSpan) -> Dict[str, Any]\n        \"\"\"\n        Returns the OTel context for Sentry.\n        See: https://develop.sentry.dev/sdk/performance/opentelemetry/#step-5-add-opentelemetry-context\n        \"\"\"\n        ctx = {}\n\n        if otel_span.attributes:\n            ctx[\"attributes\"] = dict(otel_span.attributes)\n\n        if otel_span.resource.attributes:\n            ctx[\"resource\"] = dict(otel_span.resource.attributes)\n\n        return ctx\n\n    def _get_trace_data(self, otel_span, parent_context):\n        # type: (OTelSpan, SpanContext) -> Dict[str, Any]\n        \"\"\"\n        Extracts tracing information from one OTel span and its parent OTel context.\n        \"\"\"\n        trace_data = {}\n\n        span_id = format_span_id(otel_span.context.span_id)\n        trace_data[\"span_id\"] = span_id\n\n        trace_id = format_trace_id(otel_span.context.trace_id)\n        trace_data[\"trace_id\"] = trace_id\n\n        parent_span_id = (\n            format_span_id(otel_span.parent.span_id) if otel_span.parent else None\n        )\n        trace_data[\"parent_span_id\"] = parent_span_id\n\n        sentry_trace_data = get_value(SENTRY_TRACE_KEY, parent_context)\n        trace_data[\"parent_sampled\"] = (\n            sentry_trace_data[\"parent_sampled\"] if sentry_trace_data else None\n        )\n\n        baggage = get_value(SENTRY_BAGGAGE_KEY, parent_context)\n        trace_data[\"baggage\"] = baggage\n\n        return trace_data\n\n    def _update_span_with_otel_data(self, sentry_span, otel_span):\n        # type: (SentrySpan, OTelSpan) -> None\n        \"\"\"\n        Convert OTel span data and update the Sentry span with it.\n        This should eventually happen on the server when ingesting the spans.\n        \"\"\"\n        for key, val in otel_span.attributes.items():\n            sentry_span.set_data(key, val)\n\n        sentry_span.set_data(\"otel.kind\", otel_span.kind)\n\n        op = otel_span.name\n        description = otel_span.name\n\n        http_method = otel_span.attributes.get(SpanAttributes.HTTP_METHOD, None)\n        db_query = otel_span.attributes.get(SpanAttributes.DB_SYSTEM, None)\n\n        if http_method:\n            op = \"http\"\n\n            if otel_span.kind == SpanKind.SERVER:\n                op += \".server\"\n            elif otel_span.kind == SpanKind.CLIENT:\n                op += \".client\"\n\n            description = http_method\n\n            peer_name = otel_span.attributes.get(SpanAttributes.NET_PEER_NAME, None)\n            if peer_name:\n                description += \" {}\".format(peer_name)\n\n            target = otel_span.attributes.get(SpanAttributes.HTTP_TARGET, None)\n            if target:\n                description += \" {}\".format(target)\n\n            if not peer_name and not target:\n                url = otel_span.attributes.get(SpanAttributes.HTTP_URL, None)\n                if url:\n                    parsed_url = urlparse(url)\n                    url = f\"{parsed_url.scheme}://{parsed_url.netloc}{parsed_url.path}\"\n                    description += \" {}\".format(url)\n\n            status_code = otel_span.attributes.get(\n                SpanAttributes.HTTP_STATUS_CODE, None\n            )\n            if status_code:\n                sentry_span.set_http_status(status_code)\n\n        elif db_query:\n            op = \"db\"\n            statement = otel_span.attributes.get(SpanAttributes.DB_STATEMENT, None)\n            if statement:\n                description = statement\n\n        sentry_span.op = op\n        sentry_span.description = description\n", "path": "sentry_sdk/integrations/opentelemetry/span_processor.py"}]}