def handle_data(self, request, organization, project_ids, results): if not results: return results first_row = results[0] # TODO(mark) move all of this result formatting into discover.query() # once those APIs are used across the application. if "transaction.status" in first_row: for row in results: row["transaction.status"] = SPAN_STATUS_CODE_TO_NAME.get( row["transaction.status"]) fields = request.GET.getlist("field") has_issues = "issue" in fields if has_issues: # Look up the short ID and return that in the results if has_issues: issue_ids = set(row.get("issue.id") for row in results) issues = Group.issues_mapping(issue_ids, project_ids, organization) for result in results: if has_issues and "issue.id" in result: result["issue"] = issues.get(result["issue.id"], "unknown") if not ("project.id" in first_row or "projectid" in first_row): return results for result in results: for key in ("projectid", "project.id"): if key in result: if key not in fields: del result[key] return results
def handle_data( self, request: Request, organization: Organization, project_ids: Sequence[int], results: Optional[Sequence[Any]], ) -> Optional[Sequence[Any]]: if not results: return results first_row = results[0] # TODO(mark) move all of this result formatting into discover.query() # once those APIs are used across the application. if "transaction.status" in first_row: for row in results: row["transaction.status"] = SPAN_STATUS_CODE_TO_NAME.get(row["transaction.status"]) fields = self.get_field_list(organization, request) if "issue" in fields: # Look up the short ID and return that in the results self.handle_issues(results, project_ids, organization) if not ("project.id" in first_row or "projectid" in first_row): return results for result in results: for key in ("projectid", "project.id"): if key in result and key not in fields: del result[key] return results
def full_dict(self, detailed: bool = False) -> FullResponse: result = cast(FullResponse, self.to_dict()) if detailed and "transaction.status" in self.event: result.update({ "transaction.status": SPAN_STATUS_CODE_TO_NAME.get(self.event["transaction.status"], "unknown"), }) if self.nodestore_event: result["timestamp"] = self.nodestore_event.data.get("timestamp") result["start_timestamp"] = self.nodestore_event.data.get( "start_timestamp") if detailed: if "measurements" in self.nodestore_event.data: result["measurements"] = self.nodestore_event.data.get( "measurements") result["_meta"] = {} result["tags"], result["_meta"]["tags"] = get_tags_with_meta( self.nodestore_event) # Only add children that have nodestore events, which may be missing if we're pruning for trace navigator result["children"] = [ child.full_dict(detailed) for child in self.children if child.nodestore_event ] return result
def handle_data(self, request, organization, project_ids, results): if not results: return results first_row = results[0] # TODO(mark) move all of this result formatting into discover.query() # once those APIs are used across the application. tests = { "transaction.status": "transaction.status" in first_row, "trace": "trace" in first_row, } if any(tests.values()): for row in results: if tests["transaction.status"]: row["transaction.status"] = SPAN_STATUS_CODE_TO_NAME.get( row["transaction.status"] ) if tests["trace"]: row["trace"] = uuid.UUID(row["trace"]).hex fields = request.GET.getlist("field") issues = {} if "issue" in fields: # Look up the short ID and return that in the results issue_ids = set(row["issue.id"] for row in results) issues = { i.id: i.qualified_short_id for i in Group.objects.filter( id__in=issue_ids, project_id__in=project_ids, project__organization=organization ) } for result in results: if "issue.id" in result: result["issue"] = issues[result["issue.id"]] if not ("project.id" in first_row or "projectid" in first_row): return results projects = { p["id"]: p["slug"] for p in Project.objects.filter(organization=organization, id__in=project_ids).values( "id", "slug" ) } for result in results: for key in ("projectid", "project.id"): if key in result: # Handle bizarre empty case if result[key] == 0: result["project.name"] = "" else: result["project.name"] = projects[result[key]] if key not in fields: del result[key] return results
def serialize_event(self, event, *args, **kwargs): result = super().serialize_event(event, *args, **kwargs) if "transaction.status" in event: result.update({ "transaction.status": SPAN_STATUS_CODE_TO_NAME.get(event["transaction.status"], "unknown"), }) result.update({ "children": [], }) return result
def handle_data(self, request, organization, project_ids, results, omit_nan=False): if not results: return results first_row = results[0] # TODO(mark) move all of this result formatting into discover.query() # once those APIs are used across the application. if "transaction.status" in first_row: for row in results: row["transaction.status"] = SPAN_STATUS_CODE_TO_NAME.get(row["transaction.status"]) fields = request.GET.getlist("field") has_issues = "issue" in fields if has_issues or omit_nan: # Look up the short ID and return that in the results if has_issues: issue_ids = set(row.get("issue.id") for row in results) issues = Group.issues_mapping(issue_ids, project_ids, organization) for result in results: if has_issues and "issue.id" in result: result["issue"] = issues.get(result["issue.id"], "unknown") # Remove any potential NaN or Inf cause python json accepts either, but js doesn't if omit_nan: for key in result.keys(): if isinstance(result[key], float) and ( math.isnan(result[key]) or math.isinf(result[key]) ): result[key] = None if not ("project.id" in first_row or "projectid" in first_row): return results for result in results: for key in ("projectid", "project.id"): if key in result: if key not in fields: del result[key] return results
def handle_data(self, request, organization, project_ids, results): if not results: return results first_row = results[0] # TODO(mark) move all of this result formatting into discover.query() # once those APIs are used across the application. if "transaction.status" in first_row: for row in results: row["transaction.status"] = SPAN_STATUS_CODE_TO_NAME.get( row["transaction.status"]) fields = request.GET.getlist("field") issues = {} if "issue" in fields: # Look up the short ID and return that in the results issue_ids = set(row["issue.id"] for row in results) issues = { i.id: i.qualified_short_id for i in Group.objects.filter( id__in=issue_ids, project_id__in=project_ids, project__organization=organization) } for result in results: if "issue.id" in result: result["issue"] = issues.get(result["issue.id"], "unknown") if not ("project.id" in first_row or "projectid" in first_row): return results for result in results: for key in ("projectid", "project.id"): if key in result: if key not in fields: del result[key] return results
def handle_data(self, request, organization, project_ids, results): if not results: return results first_row = results[0] # TODO(mark) move all of this result formatting into discover.query() # once those APIs are used across the application. tests = { "transaction.status": "transaction.status" in first_row, "trace": "trace" in first_row, } if any(tests.values()): for row in results: if tests["transaction.status"]: row["transaction.status"] = SPAN_STATUS_CODE_TO_NAME.get( row["transaction.status"] ) if tests["trace"]: row["trace"] = uuid.UUID(row["trace"]).hex if not ("project.id" in first_row or "projectid" in first_row): return results fields = request.GET.getlist("field") projects = { p["id"]: p["slug"] for p in Project.objects.filter(organization=organization, id__in=project_ids).values( "id", "slug" ) } for result in results: for key in ("projectid", "project.id"): if key in result: result["project.name"] = projects[result[key]] if key not in fields: del result[key] return results
def get_tag_value_paginator_for_projects( self, projects, environments, key, start=None, end=None, query=None, order_by="-last_seen", include_transactions=False, ): from sentry.api.paginator import SequencePaginator if not order_by == "-last_seen": raise ValueError("Unsupported order_by: %s" % order_by) dataset = Dataset.Events if include_transactions: dataset = Dataset.Discover snuba_key = snuba.get_snuba_column_name(key, dataset=dataset) # We cannot search the values of these columns like we do other columns because they are # a different type, and as such, LIKE and != do not work on them. Furthermore, because the # use case for these values in autosuggestion is minimal, so we choose to disable them here. # # event_id: This is a FixedString which disallows us to use LIKE on it when searching, # but does work with !=. However, for consistency sake we disallow it # entirely, furthermore, suggesting an event_id is not a very useful feature # as they are not human readable. # timestamp: This is a DateTime which disallows us to use both LIKE and != on it when # searching. Suggesting a timestamp can potentially be useful but as it does # work at all, we opt to disable it here. A potential solution can be to # generate a time range to bound where they are searching. e.g. if a user # enters 2020-07 we can generate the following conditions: # >= 2020-07-01T00:00:00 AND <= 2020-07-31T23:59:59 # time: This is a column computed from timestamp so it suffers the same issues if snuba_key in {"event_id", "timestamp", "time"}: return SequencePaginator([]) # These columns have fixed values and we don't need to emit queries to find out the # potential options. if key in {"error.handled", "error.unhandled"}: return SequencePaginator([ ( 1, TagValue(key=key, value="true", times_seen=None, first_seen=None, last_seen=None), ), ( 2, TagValue(key=key, value="false", times_seen=None, first_seen=None, last_seen=None), ), ]) conditions = [] # transaction status needs a special case so that the user interacts with the names and not codes transaction_status = snuba_key == "transaction_status" if include_transactions and transaction_status: # Here we want to use the status codes during filtering, # but want to do this with names that include our query status_codes = [ span_key for span_key, value in SPAN_STATUS_CODE_TO_NAME.items() if (query and query in value) or (not query) ] if status_codes: conditions.append([snuba_key, "IN", status_codes]) else: return SequencePaginator([]) elif key in FUZZY_NUMERIC_KEYS: converted_query = int( query) if query is not None and query.isdigit() else None if converted_query is not None: conditions.append([ snuba_key, ">=", converted_query - FUZZY_NUMERIC_DISTANCE ]) conditions.append([ snuba_key, "<=", converted_query + FUZZY_NUMERIC_DISTANCE ]) elif include_transactions and key == PROJECT_ALIAS: project_filters = { "id__in": projects, } if query: project_filters["slug__icontains"] = query project_queryset = Project.objects.filter( **project_filters).values("id", "slug") if not project_queryset.exists(): return SequencePaginator([]) project_slugs = { project["id"]: project["slug"] for project in project_queryset } projects = [project["id"] for project in project_queryset] snuba_key = "project_id" else: snuba_name = snuba_key is_user_alias = include_transactions and key == USER_DISPLAY_ALIAS if is_user_alias: # user.alias is a pseudo column in discover. It is computed by coalescing # together multiple user attributes. Here we get the coalese function used, # and resolve it to the corresponding snuba query resolver = snuba.resolve_column(dataset) snuba_name = FIELD_ALIASES[USER_DISPLAY_ALIAS].get_field() snuba.resolve_complex_column(snuba_name, resolver) elif snuba_name in BLACKLISTED_COLUMNS: snuba_name = f"tags[{key}]" if query: conditions.append([snuba_name, "LIKE", f"%{query}%"]) else: conditions.append([snuba_name, "!=", ""]) filters = {"project_id": projects} if environments: filters["environment"] = environments if dataset == Dataset.Events: conditions.append(DEFAULT_TYPE_CONDITION) results = snuba.query( dataset=dataset, start=start, end=end, groupby=[snuba_key], filter_keys=filters, aggregations=[ ["count()", "", "times_seen"], ["min", "timestamp", "first_seen"], ["max", "timestamp", "last_seen"], ], conditions=conditions, orderby=order_by, # TODO: This means they can't actually paginate all TagValues. limit=1000, arrayjoin=snuba.get_arrayjoin(snuba_key), referrer="tagstore.get_tag_value_paginator_for_projects", ) if include_transactions: # With transaction_status we need to map the ids back to their names if transaction_status: results = OrderedDict([ (SPAN_STATUS_CODE_TO_NAME[result_key], data) for result_key, data in results.items() ]) # With project names we map the ids back to the project slugs elif key == PROJECT_ALIAS: results = OrderedDict([(project_slugs[value], data) for value, data in results.items() if value in project_slugs]) tag_values = [ TagValue(key=key, value=str(value), **fix_tag_value_data(data)) for value, data in results.items() ] desc = order_by.startswith("-") score_field = order_by.lstrip("-") return SequencePaginator( [(int(to_timestamp(getattr(tv, score_field)) * 1000), tv) for tv in tag_values], reverse=desc, )