|
@@ -0,0 +1,146 @@
|
|
|
|
+from typing import cast
|
|
|
|
+
|
|
|
|
+import sentry_sdk
|
|
|
|
+from rest_framework.request import Request
|
|
|
|
+from rest_framework.response import Response
|
|
|
|
+
|
|
|
|
+from sentry import features, options
|
|
|
|
+from sentry.api.api_owners import ApiOwner
|
|
|
|
+from sentry.api.api_publish_status import ApiPublishStatus
|
|
|
|
+from sentry.api.base import region_silo_endpoint
|
|
|
|
+from sentry.api.bases import NoProjects, OrganizationEventsV2EndpointBase
|
|
|
|
+from sentry.api.paginator import SequencePaginator
|
|
|
|
+from sentry.api.serializers import serialize
|
|
|
|
+from sentry.api.utils import handle_query_errors
|
|
|
|
+from sentry.search.events.builder import SpansIndexedQueryBuilder
|
|
|
|
+from sentry.search.events.types import ParamsType, QueryBuilderConfig
|
|
|
|
+from sentry.snuba.dataset import Dataset
|
|
|
|
+from sentry.snuba.referrer import Referrer
|
|
|
|
+from sentry.tagstore.types import TagKey, TagValue
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@region_silo_endpoint
|
|
|
|
+class OrganizationSpansFieldsEndpoint(OrganizationEventsV2EndpointBase):
|
|
|
|
+ publish_status = {
|
|
|
|
+ "GET": ApiPublishStatus.PRIVATE,
|
|
|
|
+ }
|
|
|
|
+ owner = ApiOwner.PERFORMANCE
|
|
|
|
+
|
|
|
|
+ def get(self, request: Request, organization) -> Response:
|
|
|
|
+ if not features.has(
|
|
|
|
+ "organizations:performance-trace-explorer", organization, actor=request.user
|
|
|
|
+ ):
|
|
|
|
+ return Response(status=404)
|
|
|
|
+
|
|
|
|
+ try:
|
|
|
|
+ snuba_params, params = self.get_snuba_dataclass(request, organization)
|
|
|
|
+ except NoProjects:
|
|
|
|
+ return self.paginate(
|
|
|
|
+ request=request,
|
|
|
|
+ paginator=SequencePaginator([]),
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ max_span_tags = options.get("performance.spans-tags-key.max")
|
|
|
|
+
|
|
|
|
+ with handle_query_errors():
|
|
|
|
+ # This has the limitations that we cannot paginate and
|
|
|
|
+ # we do not provide any guarantees around which tag keys
|
|
|
|
+ # are returned if the total exceeds the limit.
|
|
|
|
+ builder = SpansIndexedQueryBuilder(
|
|
|
|
+ Dataset.SpansIndexed,
|
|
|
|
+ params=cast(ParamsType, params),
|
|
|
|
+ snuba_params=snuba_params,
|
|
|
|
+ query=None,
|
|
|
|
+ selected_columns=["array_join(tags.key)"],
|
|
|
|
+ orderby=None,
|
|
|
|
+ limitby=("array_join(tags.key)", 1),
|
|
|
|
+ limit=max_span_tags,
|
|
|
|
+ sample_rate=options.get("performance.spans-tags-key.sample-rate"),
|
|
|
|
+ config=QueryBuilderConfig(
|
|
|
|
+ transform_alias_to_input_format=True,
|
|
|
|
+ functions_acl=["array_join"],
|
|
|
|
+ ),
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ results = builder.process_results(builder.run_query(Referrer.API_SPANS_TAG_KEYS.value))
|
|
|
|
+
|
|
|
|
+ paginator = SequencePaginator(
|
|
|
|
+ [
|
|
|
|
+ # TODO: prepend the list of sentry defined fields here
|
|
|
|
+ (row["array_join(tags.key)"], TagKey(row["array_join(tags.key)"]))
|
|
|
|
+ for row in results["data"]
|
|
|
|
+ ]
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ return self.paginate(
|
|
|
|
+ request=request,
|
|
|
|
+ paginator=paginator,
|
|
|
|
+ on_results=lambda results: serialize(results, request.user),
|
|
|
|
+ default_per_page=max_span_tags,
|
|
|
|
+ max_per_page=max_span_tags,
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+@region_silo_endpoint
|
|
|
|
+class OrganizationSpansFieldValuesEndpoint(OrganizationEventsV2EndpointBase):
|
|
|
|
+ publish_status = {
|
|
|
|
+ "GET": ApiPublishStatus.PRIVATE,
|
|
|
|
+ }
|
|
|
|
+ owner = ApiOwner.PERFORMANCE
|
|
|
|
+
|
|
|
|
+ def get(self, request: Request, organization, key: str) -> Response:
|
|
|
|
+ if not features.has(
|
|
|
|
+ "organizations:performance-trace-explorer", organization, actor=request.user
|
|
|
|
+ ):
|
|
|
|
+ return Response(status=404)
|
|
|
|
+
|
|
|
|
+ try:
|
|
|
|
+ snuba_params, params = self.get_snuba_dataclass(request, organization)
|
|
|
|
+ except NoProjects:
|
|
|
|
+ return self.paginate(
|
|
|
|
+ request=request,
|
|
|
|
+ paginator=SequencePaginator([]),
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ sentry_sdk.set_tag("query.tag_key", key)
|
|
|
|
+
|
|
|
|
+ max_span_tags = options.get("performance.spans-tags-values.max")
|
|
|
|
+
|
|
|
|
+ with handle_query_errors():
|
|
|
|
+ builder = SpansIndexedQueryBuilder(
|
|
|
|
+ Dataset.SpansIndexed,
|
|
|
|
+ params=cast(ParamsType, params),
|
|
|
|
+ snuba_params=snuba_params,
|
|
|
|
+ query=None,
|
|
|
|
+ selected_columns=[key, "count()", "min(timestamp)", "max(timestamp)"],
|
|
|
|
+ orderby="-count()",
|
|
|
|
+ limit=max_span_tags,
|
|
|
|
+ sample_rate=options.get("performance.spans-tags-key.sample-rate"),
|
|
|
|
+ config=QueryBuilderConfig(
|
|
|
|
+ transform_alias_to_input_format=True,
|
|
|
|
+ ),
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ results = builder.process_results(builder.run_query(Referrer.API_SPANS_TAG_KEYS.value))
|
|
|
|
+
|
|
|
|
+ paginator = SequencePaginator(
|
|
|
|
+ [
|
|
|
|
+ (
|
|
|
|
+ row[key],
|
|
|
|
+ TagValue(
|
|
|
|
+ key=key,
|
|
|
|
+ value=row[key],
|
|
|
|
+ times_seen=row["count()"],
|
|
|
|
+ first_seen=row["min(timestamp)"],
|
|
|
|
+ last_seen=row["max(timestamp)"],
|
|
|
|
+ ),
|
|
|
|
+ )
|
|
|
|
+ for row in results["data"]
|
|
|
|
+ ]
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ return self.paginate(
|
|
|
|
+ request=request,
|
|
|
|
+ paginator=paginator,
|
|
|
|
+ on_results=lambda results: serialize(results, request.user),
|
|
|
|
+ )
|