1
+ from time import time
2
+
1
3
from opentelemetry .context import get_value # type: ignore
2
4
from opentelemetry .sdk .trace import SpanProcessor # type: ignore
3
5
from opentelemetry .semconv .trace import SpanAttributes # type: ignore
33
35
from sentry_sdk ._types import Event , Hint
34
36
35
37
OPEN_TELEMETRY_CONTEXT = "otel"
38
+ SPAN_MAX_TIME_OPEN_MINUTES = 10
36
39
37
40
38
41
def link_trace_context_to_error_event (event , otel_span_map ):
@@ -76,6 +79,9 @@ class SentrySpanProcessor(SpanProcessor): # type: ignore
76
79
# The mapping from otel span ids to sentry spans
77
80
otel_span_map = {} # type: Dict[str, Union[Transaction, SentrySpan]]
78
81
82
+ # The currently open spans. Elements will be discarded after SPAN_MAX_TIME_OPEN_MINUTES
83
+ open_spans = {} # type: dict[int, set[str]]
84
+
79
85
def __new__ (cls ):
80
86
# type: () -> SentrySpanProcessor
81
87
if not hasattr (cls , "instance" ):
@@ -90,6 +96,24 @@ def global_event_processor(event, hint):
90
96
# type: (Event, Hint) -> Event
91
97
return link_trace_context_to_error_event (event , self .otel_span_map )
92
98
99
+ def _prune_old_spans (self ):
100
+ # type: (SentrySpanProcessor) -> None
101
+ """
102
+ Prune spans that have been open for too long.
103
+ """
104
+ current_time_minutes = int (time () / 60 )
105
+ for span_start_minutes in list (
106
+ self .open_spans .keys ()
107
+ ): # making a list because we change the dict
108
+ # prune empty open spans buckets
109
+ if self .open_spans [span_start_minutes ] == set ():
110
+ self .open_spans .pop (span_start_minutes )
111
+
112
+ # prune old buckets
113
+ elif current_time_minutes - span_start_minutes > SPAN_MAX_TIME_OPEN_MINUTES :
114
+ for span_id in self .open_spans .pop (span_start_minutes ):
115
+ self .otel_span_map .pop (span_id , None )
116
+
93
117
def on_start (self , otel_span , parent_context = None ):
94
118
# type: (OTelSpan, Optional[SpanContext]) -> None
95
119
hub = Hub .current
@@ -125,7 +149,9 @@ def on_start(self, otel_span, parent_context=None):
125
149
sentry_span = sentry_parent_span .start_child (
126
150
span_id = trace_data ["span_id" ],
127
151
description = otel_span .name ,
128
- start_timestamp = utc_from_timestamp (otel_span .start_time / 1e9 ),
152
+ start_timestamp = utc_from_timestamp (
153
+ otel_span .start_time / 1e9
154
+ ), # OTel spans have nanosecond precision
129
155
instrumenter = INSTRUMENTER .OTEL ,
130
156
)
131
157
else :
@@ -135,12 +161,22 @@ def on_start(self, otel_span, parent_context=None):
135
161
parent_span_id = parent_span_id ,
136
162
trace_id = trace_data ["trace_id" ],
137
163
baggage = trace_data ["baggage" ],
138
- start_timestamp = utc_from_timestamp (otel_span .start_time / 1e9 ),
164
+ start_timestamp = utc_from_timestamp (
165
+ otel_span .start_time / 1e9
166
+ ), # OTel spans have nanosecond precision
139
167
instrumenter = INSTRUMENTER .OTEL ,
140
168
)
141
169
142
170
self .otel_span_map [trace_data ["span_id" ]] = sentry_span
143
171
172
+ span_start_in_minutes = int (
173
+ otel_span .start_time / 1e9 / 60
174
+ ) # OTel spans have nanosecond precision
175
+ self .open_spans .setdefault (span_start_in_minutes , set ()).add (
176
+ trace_data ["span_id" ]
177
+ )
178
+ self ._prune_old_spans ()
179
+
144
180
def on_end (self , otel_span ):
145
181
# type: (OTelSpan) -> None
146
182
hub = Hub .current
@@ -173,7 +209,15 @@ def on_end(self, otel_span):
173
209
else :
174
210
self ._update_span_with_otel_data (sentry_span , otel_span )
175
211
176
- sentry_span .finish (end_timestamp = utc_from_timestamp (otel_span .end_time / 1e9 ))
212
+ sentry_span .finish (
213
+ end_timestamp = utc_from_timestamp (otel_span .end_time / 1e9 )
214
+ ) # OTel spans have nanosecond precision
215
+
216
+ span_start_in_minutes = int (
217
+ otel_span .start_time / 1e9 / 60
218
+ ) # OTel spans have nanosecond precision
219
+ self .open_spans .setdefault (span_start_in_minutes , set ()).discard (span_id )
220
+ self ._prune_old_spans ()
177
221
178
222
def _is_sentry_span (self , hub , otel_span ):
179
223
# type: (Hub, OTelSpan) -> bool
0 commit comments