def build_span_tree(span_tree, spans, parent_span_id): for span_id, child in sorted(span_tree.items(), key=lambda item: item[0]): span = copy.deepcopy(reference_span) # non-leaf node span span["parent_span_id"] = parent_span_id.ljust(16, "0") span["span_id"] = span_id.ljust(16, "0") (start_delta, span_length) = time_offsets.get(span_id, (timedelta(), timedelta())) span_start_time = start_datetime + start_delta span["start_timestamp"] = timestamp_format(span_start_time) span["timestamp"] = timestamp_format(span_start_time + span_length) spans.append(span) if isinstance(child, dict): spans = build_span_tree(child, spans, span_id) elif isinstance(child, six.string_types): parent_span_id = span_id span_id = child span = copy.deepcopy(reference_span) # leaf node span span["parent_span_id"] = parent_span_id.ljust(16, "0") span["span_id"] = span_id.ljust(16, "0") (start_delta, span_length) = time_offsets.get(span_id, (timedelta(), timedelta())) span_start_time = start_datetime + start_delta span["start_timestamp"] = timestamp_format(span_start_time) span["timestamp"] = timestamp_format(span_start_time + span_length) spans.append(span) return spans
def regress_spans(original_spans): spans = [] last_index = len(original_spans) - 1 for index, reference_span in enumerate(original_spans): end_datetime = datetime.utcfromtimestamp( reference_span["timestamp"]).replace(tzinfo=pytz.utc) span = copy.deepcopy(reference_span) if index % 2 == 0: # for every even indexed span, increase its duration. # this implies the span was slower. regression_time = timedelta(milliseconds=10 + index) span["timestamp"] = timestamp_format(end_datetime + regression_time) else: # for every odd indexed span, decrease its duration. # this implies the span was faster regression_time = timedelta(milliseconds=5 + index) span["timestamp"] = timestamp_format(end_datetime - regression_time) if index == last_index: # change the op name of the last span. # the last span would be removed from the baseline transaction; # and the last span of the baseline transaction would be missing from # the regression transaction span["op"] = "resource" spans.append(span) return spans
def test_transaction(self): event_data = { "event_id": "d2132d31b39445f1938d7e21b6bf0ec4", "type": "transaction", "transaction": "/organizations/:orgId/performance/:eventSlug/", "start_timestamp": iso_format(before_now(minutes=1, milliseconds=500)), "timestamp": iso_format(before_now(minutes=1)), "contexts": { "trace": { "trace_id": "ff62a8b040f340bda5d830223def1d81", "span_id": "8f5a2b8768cafb4e", "type": "trace", } }, "spans": [ { "description": "<OrganizationContext>", "op": "react.mount", "parent_span_id": "8f5a2b8768cafb4e", "span_id": "bd429c44b67a3eb4", "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=250)), "timestamp": timestamp_format(before_now(minutes=1)), "trace_id": "ff62a8b040f340bda5d830223def1d81", }, { "description": "browser span", "op": "browser", "parent_span_id": "bd429c44b67a3eb4", "span_id": "a99fd04e79e17631", "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)), "timestamp": timestamp_format(before_now(minutes=1)), "trace_id": "ff62a8b040f340bda5d830223def1d81", }, { "description": "resource span", "op": "resource", "parent_span_id": "bd429c44b67a3eb4", "span_id": "a71a5e67db5ce938", "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)), "timestamp": timestamp_format(before_now(minutes=1)), "trace_id": "ff62a8b040f340bda5d830223def1d81", }, { "description": "http span", "op": "http", "parent_span_id": "a99fd04e79e17631", "span_id": "abe79ad9292b90a9", "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)), "timestamp": timestamp_format(before_now(minutes=1)), "trace_id": "ff62a8b040f340bda5d830223def1d81", }, { "description": "db span", "op": "db", "parent_span_id": "abe79ad9292b90a9", "span_id": "9c045ea336297177", "start_timestamp": timestamp_format(before_now(minutes=1, milliseconds=200)), "timestamp": timestamp_format(before_now(minutes=1)), "trace_id": "ff62a8b040f340bda5d830223def1d81", }, ], } with Feature({ "organizations:performance-ops-breakdown": True, "organizations:performance-suspect-spans-ingestion": True, }): event = self.post_and_retrieve_event(event_data) raw_event = event.get_raw_data() exclusive_times = [ pytest.approx(50), pytest.approx(0), pytest.approx(200), pytest.approx(0), pytest.approx(200), ] assert raw_event["spans"] == [ dict(span, exclusive_time=exclusive_time, hash=hash_values([span["description"]])) for span, exclusive_time in zip(event_data["spans"], exclusive_times) ] assert raw_event["breakdowns"] == { "span_ops": { "ops.browser": { "value": pytest.approx(200) }, "ops.resource": { "value": pytest.approx(200) }, "ops.http": { "value": pytest.approx(200) }, "ops.db": { "value": pytest.approx(200) }, "total.time": { "value": pytest.approx(1050) }, } }