Skip to content

Commit

Permalink
ref: Send partial span tree instead of clearing it
Browse files Browse the repository at this point in the history
  • Loading branch information
untitaker committed Sep 19, 2019
1 parent 3a27d87 commit 57c18dc
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 26 deletions.
60 changes: 34 additions & 26 deletions sentry_sdk/tracing.py
Expand Up @@ -23,7 +23,6 @@
from typing import Dict
from typing import List
from typing import Tuple
from typing import Iterator

_traceparent_header_format_re = re.compile(
"^[ \t]*" # whitespace
Expand Down Expand Up @@ -66,27 +65,28 @@ def __iter__(self):


class _SpanRecorder(object):
__slots__ = ("maxlen", "finished_spans")
__slots__ = ("maxlen", "finished_spans", "open_span_count")

def __init__(self):
# type: () -> None
self.maxlen = 1000
self.finished_spans = None # type: Optional[List[Span]]
def __init__(self, maxlen):
# type: (int) -> None
self.maxlen = maxlen
self.open_span_count = 0 # type: int
self.finished_spans = [] # type: List[Span]

def append(self, span):
def start_span(self, span):
# type: (Span) -> None
if self.finished_spans is not None:
if len(self.finished_spans) >= self.maxlen:
# If the span tree grows too large we decided it's better to
# discard all spans instead of trying to apply some expensive
# trimming.
self.finished_spans = None
else:
self.finished_spans.append(span)

def __iter__(self):
# type: () -> Iterator[Span]
return iter(self.finished_spans or ())
# This is just so that we don't run out of memory while recording a lot
# of spans. At some point we just stop and flush out the start of the
# trace tree (i.e. the first n spans with the smallest
# start_timestamp).
self.open_span_count += 1
if self.open_span_count > self.maxlen:
span._span_recorder = None

def finish_span(self, span):
# type: (Span) -> None
self.finished_spans.append(span)


class Span(object):
Expand Down Expand Up @@ -132,18 +132,18 @@ def __init__(
self.hub = hub
self._tags = {} # type: Dict[str, str]
self._data = {} # type: Dict[str, Any]
self._span_recorder = _SpanRecorder() # type: _SpanRecorder
self.start_timestamp = datetime.now()

#: End timestamp of span
self.timestamp = None # type: Optional[datetime]

self._span_recorder = None # type: Optional[_SpanRecorder]

def init_finished_spans(self, maxlen):
# type: (int) -> None
if self._span_recorder.finished_spans is None:
self._span_recorder.finished_spans = []

self._span_recorder.maxlen = maxlen
if self._span_recorder is None:
self._span_recorder = _SpanRecorder(maxlen)
self._span_recorder.start_span(self)

def __repr__(self):
# type: () -> str
Expand Down Expand Up @@ -189,6 +189,7 @@ def new_span(self, **kwargs):
sampled=self.sampled,
**kwargs
)

rv._span_recorder = self._span_recorder
return rv

Expand Down Expand Up @@ -279,10 +280,13 @@ def finish(self, hub=None):

self.timestamp = datetime.now()

self._span_recorder.append(self)

_maybe_create_breadcrumbs_from_span(hub, self)

if self._span_recorder is None:
return None

self._span_recorder.finish_span(self)

if self.transaction is None:
# If this has no transaction set we assume there's a parent
# transaction for this span that would be flushed out eventually.
Expand Down Expand Up @@ -310,7 +314,11 @@ def finish(self, hub=None):
"contexts": {"trace": self.get_trace_context()},
"timestamp": self.timestamp,
"start_timestamp": self.start_timestamp,
"spans": [s.to_json() for s in self._span_recorder if s is not self],
"spans": [
s.to_json()
for s in self._span_recorder.finished_spans
if s is not self
],
}
)

Expand Down
15 changes: 15 additions & 0 deletions tests/test_tracing.py
Expand Up @@ -127,3 +127,18 @@ def foo():
gc.collect()

assert len(references) == expected_refcount


def test_span_trimming(sentry_init, capture_events):
sentry_init(traces_sample_rate=1.0, _experiments={"max_spans": 3})
events = capture_events()

with Hub.current.start_span(transaction="hi"):
for i in range(10):
with Hub.current.start_span(op="foo{}".format(i)):
pass

event, = events
span1, span2 = event["spans"]
assert span1["op"] == "foo0"
assert span2["op"] == "foo1"

0 comments on commit 57c18dc

Please sign in to comment.