def get_incident_event_stats(incident, start=None, end=None, windowed_stats=False): """ Gets event stats for an incident. If start/end are provided, uses that time period, otherwise uses the incident start/current_end. """ query_params = build_incident_query_params( incident, start=start, end=end, windowed_stats=windowed_stats ) time_window = incident.alert_rule.snuba_query.time_window aggregations = query_params.pop("aggregations")[0] snuba_params = [ SnubaQueryParams( aggregations=[(aggregations[0], aggregations[1], "count")], orderby="time", groupby=["time"], rollup=time_window, limit=10000, **query_params ) ] # We want to include the specific buckets for the incident start and closed times, # so that there's no need to interpolate to show them on the frontend. If they're # cleanly divisible by the `time_window` then there's no need to fetch, since # they'll be included in the standard results anyway. extra_buckets = [] if int(to_timestamp(incident.date_started)) % time_window: extra_buckets.append(incident.date_started) if incident.date_closed and int(to_timestamp(incident.date_closed)) % time_window: extra_buckets.append(incident.date_closed.replace(second=0, microsecond=0)) # We make extra queries to fetch these buckets for bucket_start in extra_buckets: extra_bucket_query_params = build_incident_query_params( incident, start=bucket_start, end=bucket_start + timedelta(seconds=time_window) ) aggregations = extra_bucket_query_params.pop("aggregations")[0] snuba_params.append( SnubaQueryParams( aggregations=[(aggregations[0], aggregations[1], "count")], limit=1, **extra_bucket_query_params ) ) results = bulk_raw_query(snuba_params, referrer="incidents.get_incident_event_stats") # Once we receive the results, if we requested extra buckets we now need to label # them with timestamp data, since the query we ran only returns the count. for extra_start, result in zip(extra_buckets, results[1:]): result["data"][0]["time"] = int(to_timestamp(extra_start)) merged_data = list(chain(*[r["data"] for r in results])) merged_data.sort(key=lambda row: row["time"]) results[0]["data"] = merged_data return SnubaTSResult( results[0], snuba_params[0].start, snuba_params[0].end, snuba_params[0].rollup )
def bulk_get_incident_event_stats(incidents, query_params_list, data_points=50): snuba_params_list = [ SnubaQueryParams( aggregations=[( query_aggregation_to_snuba[QueryAggregations( incident.aggregation)][0], query_aggregation_to_snuba[QueryAggregations( incident.aggregation)][1], "count", )], orderby="time", groupby=["time"], rollup=max(int(incident.duration.total_seconds() / data_points), 1), limit=10000, **query_param) for incident, query_param in zip(incidents, query_params_list) ] results = bulk_raw_query(snuba_params_list, referrer="incidents.get_incident_event_stats") return [ SnubaTSResult(result, snuba_params.start, snuba_params.end, snuba_params.rollup) for snuba_params, result in zip(snuba_params_list, results) ]
def test_transactions_dataset_with_project_id(self): query_params = SnubaQueryParams( dataset=Dataset.Transactions, filter_keys={"project_id": [self.project.id]}) kwargs, _, _ = _prepare_query_params(query_params) assert kwargs["project"] == [self.project.id]
def test_outcomes_dataset_with_org_id(self): query_params = SnubaQueryParams( dataset=Dataset.Outcomes, filter_keys={"org_id": [self.organization.id]}) kwargs, _, _ = _prepare_query_params(query_params) assert kwargs["organization"] == self.organization.id
def get_incident_aggregates(incident, start=None, end=None, windowed_stats=False, use_alert_aggregate=False): """ Calculates aggregate stats across the life of an incident, or the provided range. If `use_alert_aggregate` is True, calculates just the aggregate that the alert is for, and returns as the `count` key. If False, returns two values: - count: Total count of events - unique_users: Total number of unique users """ query_params = build_incident_query_params(incident, start, end, windowed_stats) if not use_alert_aggregate: query_params["aggregations"] = [ ("count()", "", "count"), ("uniq", "tags[sentry:user]", "unique_users"), ] else: query_params["aggregations"][0][2] = "count" snuba_params_list = [SnubaQueryParams(limit=10000, **query_params)] results = bulk_raw_query(snuba_params_list, referrer="incidents.get_incident_aggregates") return results[0]["data"][0]
def bulk_get_incident_event_stats(incidents, query_params_list): snuba_params_list = [ SnubaQueryParams( aggregations=[( query_aggregation_to_snuba[QueryAggregations( incident.aggregation)][0], query_aggregation_to_snuba[QueryAggregations( incident.aggregation)][1], "count", )], orderby="time", groupby=["time"], rollup=incident.alert_rule.time_window * 60 if incident.alert_rule is not None else 1 * 60, # TODO: When time_window is persisted, switch to using that instead of alert_rule.time_window. limit=10000, **query_param) for incident, query_param in zip(incidents, query_params_list) ] results = bulk_raw_query(snuba_params_list, referrer="incidents.get_incident_event_stats") return [ SnubaTSResult(result, snuba_params.start, snuba_params.end, snuba_params.rollup) for snuba_params, result in zip(snuba_params_list, results) ]
def bulk_get_incident_event_stats(incidents, query_params_list, data_points=50): snuba_params_list = [ SnubaQueryParams( aggregations=[ ('count()', '', 'count'), ], orderby='time', groupby=['time'], rollup=max(int(incident.duration.total_seconds() / data_points), 1), limit=10000, **query_param) for incident, query_param in zip(incidents, query_params_list) ] results = bulk_raw_query(snuba_params_list, referrer='incidents.get_incident_event_stats') return [ SnubaTSResult( result, snuba_params.start, snuba_params.end, snuba_params.rollup, ) for snuba_params, result in zip(snuba_params_list, results) ]
def test_outcomes_dataset_with_key_id(self): key = self.create_project_key(project=self.project) query_params = SnubaQueryParams(dataset=Dataset.Outcomes, filter_keys={"key_id": [key.id]}) kwargs, _, _ = _prepare_query_params(query_params) assert kwargs["organization"] == self.organization.id
def get_incident_event_stats(incident, start=None, end=None, windowed_stats=False): """ Gets event stats for an incident. If start/end are provided, uses that time period, otherwise uses the incident start/current_end. """ query_params = build_incident_query_params(incident, start=start, end=end, windowed_stats=windowed_stats) snuba_params = SnubaQueryParams( aggregations=[( query_aggregation_to_snuba[aggregate_to_query_aggregation[ incident.alert_rule.snuba_query.aggregate]][0], query_aggregation_to_snuba[aggregate_to_query_aggregation[ incident.alert_rule.snuba_query.aggregate]][1], "count", )], orderby="time", groupby=["time"], rollup=incident.alert_rule.snuba_query.time_window, limit=10000, **query_params) results = bulk_raw_query([snuba_params], referrer="incidents.get_incident_event_stats") return SnubaTSResult(results[0], snuba_params.start, snuba_params.end, snuba_params.rollup)
def test_with_deleted_project(self): query_params = SnubaQueryParams( dataset=Dataset.Events, filter_keys={"project_id": [self.project.id]}) self.project.delete() with pytest.raises(UnqualifiedQueryError): get_query_params_to_update_for_projects(query_params)
def test_with_some_deleted_projects(self, mock_project): other_project = self.create_project(organization=self.organization, slug="a" * 32) query_params = SnubaQueryParams( dataset=Dataset.Events, filter_keys={"project_id": [self.project.id, other_project.id]} ) other_project.delete() organization_id, _ = get_query_params_to_update_for_projects(query_params) assert organization_id == self.organization.id
def build_extra_query_params(bucket_start): extra_bucket_query_params = build_incident_query_params( incident, start=bucket_start, end=bucket_start + timedelta(seconds=time_window)) aggregations = extra_bucket_query_params.pop("aggregations")[0] return SnubaQueryParams(aggregations=[(aggregations[0], aggregations[1], "count")], limit=1, **extra_bucket_query_params)
def bulk_get_incident_aggregates(query_params_list): snuba_params_list = [ SnubaQueryParams(aggregations=[("count()", "", "count"), ("uniq", "tags[sentry:user]", "unique_users")], limit=10000, **query_param) for query_param in query_params_list ] results = bulk_raw_query(snuba_params_list, referrer="incidents.get_incident_aggregates") return [result["data"][0] for result in results]
def bulk_get_incident_aggregates(query_params_list): snuba_params_list = [ SnubaQueryParams(aggregations=[ ('count()', '', 'count'), ('uniq', 'tags[sentry:user]', 'unique_users'), ], limit=10000, **query_param) for query_param in query_params_list ] results = bulk_raw_query(snuba_params_list, referrer='incidents.get_incident_aggregates') return [result['data'][0] for result in results]
def test_original_query_params_does_not_get_mutated(self): snuba_params = SnubaQueryParams( dataset=Dataset.Sessions, start=datetime.now() - timedelta(hours=1), end=datetime.now(), groupby=[], conditions=[[["environment", "IN", {"development", "abc"}]]], filter_keys={"release": [self.create_release(version="1.0.0").id]}, aggregations=[], rollup=86400, is_grouprelease=False, **{"selected_columns": ["sessions"]}, ) conditions = [[["environment", "IN", {"development", "abc"}]]] kwargs = {"selected_columns": ["sessions"]} _prepare_query_params(snuba_params) assert conditions == snuba_params.conditions assert kwargs == snuba_params.kwargs
def get_incident_aggregates(incident, start=None, end=None, windowed_stats=False): """ Calculates aggregate stats across the life of an incident, or the provided range. - count: Total count of events - unique_users: Total number of unique users """ query_params = build_incident_query_params(incident, start, end, windowed_stats) # We don't care about the specific aggregations here, we just want total event and # unique user counts query_params.pop("aggregations", None) snuba_params_list = [ SnubaQueryParams( aggregations=[("count()", "", "count"), ("uniq", "tags[sentry:user]", "unique_users")], limit=10000, **query_params ) ] results = bulk_raw_query(snuba_params_list, referrer="incidents.get_incident_aggregates") return [result["data"][0] for result in results][0]
def bulk_get_incident_event_stats(incidents, query_params_list): snuba_params_list = [ SnubaQueryParams(aggregations=[( query_aggregation_to_snuba[aggregate_to_query_aggregation[ incident.alert_rule.snuba_query.aggregate]][0], query_aggregation_to_snuba[aggregate_to_query_aggregation[ incident.alert_rule.snuba_query.aggregate]][1], "count", )], orderby="time", groupby=["time"], rollup=incident.alert_rule.snuba_query.time_window, limit=10000, **query_param) for incident, query_param in zip(incidents, query_params_list) ] results = bulk_raw_query(snuba_params_list, referrer="incidents.get_incident_event_stats") return [ SnubaTSResult(result, snuba_params.start, snuba_params.end, snuba_params.rollup) for snuba_params, result in zip(snuba_params_list, results) ]
def test_invalid_dataset_provided(self): query_params = SnubaQueryParams(dataset="invalid_dataset") with pytest.raises(UnqualifiedQueryError): _prepare_query_params(query_params)
def test_outcomes_dataset_with_no_org_id_given(self): query_params = SnubaQueryParams(dataset=Dataset.Outcomes) with pytest.raises(UnqualifiedQueryError): _prepare_query_params(query_params)
def query_trace_data( trace_id: str, params: Mapping[str, str] ) -> Tuple[Sequence[SnubaTransaction], Sequence[SnubaError]]: transaction_query = discover.prepare_discover_query( selected_columns=[ "id", "transaction.status", "transaction.op", "transaction.duration", "transaction", "timestamp", # project gets the slug, and project.id gets added automatically "project", "trace.span", "trace.parent_span", 'to_other(trace.parent_span, "", 0, 1) AS root', ], # We want to guarantee at least getting the root, and hopefully events near it with timestamp # id is just for consistent results orderby=["-root", "timestamp", "id"], params=params, query=f"event.type:transaction trace:{trace_id}", ) error_query = discover.prepare_discover_query( selected_columns=[ "id", "project", "timestamp", "trace.span", "transaction", "issue", "title", "tags[level]", ], # Don't add timestamp to this orderby as snuba will have to split the time range up and make multiple queries orderby=["id"], params=params, query=f"!event.type:transaction trace:{trace_id}", auto_fields=False, ) snuba_params = [ SnubaQueryParams( dataset=Dataset.Discover, start=snuba_filter.start, end=snuba_filter.end, groupby=snuba_filter.groupby, conditions=snuba_filter.conditions, filter_keys=snuba_filter.filter_keys, aggregations=snuba_filter.aggregations, selected_columns=snuba_filter.selected_columns, having=snuba_filter.having, orderby=snuba_filter.orderby, limit=MAX_TRACE_SIZE, ) for snuba_filter in [transaction_query.filter, error_query.filter] ] results = bulk_raw_query( snuba_params, referrer="api.trace-view.get-events", ) transformed_results = [ discover.transform_results(result, query.fields["functions"], query.columns, query.filter)[ "data" ] for result, query in zip(results, [transaction_query, error_query]) ] return cast(Sequence[SnubaTransaction], transformed_results[0]), cast( Sequence[SnubaError], transformed_results[1] )