types.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. import {t} from 'sentry/locale';
  2. import type {AggregationOutputType} from 'sentry/utils/discover/fields';
  3. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  4. import type {FieldDefinition} from 'sentry/utils/fields';
  5. import {FieldKind, FieldValueType} from 'sentry/utils/fields';
  6. export enum StarfishType {
  7. BACKEND = 'backend',
  8. MOBILE = 'mobile',
  9. FRONTEND = 'frontend',
  10. }
  11. export enum ModuleName {
  12. HTTP = 'http',
  13. DB = 'db',
  14. CACHE = 'cache',
  15. VITAL = 'vital',
  16. QUEUE = 'queue',
  17. SCREEN_LOAD = 'screen_load',
  18. APP_START = 'app_start',
  19. RESOURCE = 'resource',
  20. AI = 'ai',
  21. MOBILE_UI = 'mobile-ui',
  22. ALL = '',
  23. OTHER = 'other',
  24. }
  25. export enum SpanMetricsField {
  26. SPAN_OP = 'span.op',
  27. SPAN_DESCRIPTION = 'span.description',
  28. SPAN_MODULE = 'span.module',
  29. SPAN_ACTION = 'span.action',
  30. SPAN_DOMAIN = 'span.domain',
  31. SPAN_GROUP = 'span.group',
  32. SPAN_DURATION = 'span.duration',
  33. SPAN_SELF_TIME = 'span.self_time',
  34. PROJECT = 'project',
  35. PROJECT_ID = 'project.id',
  36. TRANSACTION = 'transaction',
  37. RESOURCE_RENDER_BLOCKING_STATUS = 'resource.render_blocking_status',
  38. HTTP_RESPONSE_CONTENT_LENGTH = 'http.response_content_length',
  39. HTTP_DECODED_RESPONSE_CONTENT_LENGTH = 'http.decoded_response_content_length',
  40. HTTP_RESPONSE_TRANSFER_SIZE = 'http.response_transfer_size',
  41. FILE_EXTENSION = 'file_extension',
  42. AI_TOTAL_TOKENS_USED = 'ai.total_tokens.used',
  43. AI_TOTAL_COST = 'ai.total_cost',
  44. OS_NAME = 'os.name',
  45. APP_START_TYPE = 'app_start_type',
  46. DEVICE_CLASS = 'device.class',
  47. CACHE_HIT = 'cache.hit',
  48. CACHE_ITEM_SIZE = 'cache.item_size',
  49. MESSAGING_MESSAGE_RECEIVE_LATENCY = 'messaging.message.receive.latency',
  50. }
  51. export type SpanNumberFields =
  52. | SpanMetricsField.AI_TOTAL_COST
  53. | SpanMetricsField.AI_TOTAL_TOKENS_USED
  54. | SpanMetricsField.SPAN_SELF_TIME
  55. | SpanMetricsField.SPAN_DURATION
  56. | SpanMetricsField.HTTP_DECODED_RESPONSE_CONTENT_LENGTH
  57. | SpanMetricsField.HTTP_RESPONSE_CONTENT_LENGTH
  58. | SpanMetricsField.HTTP_RESPONSE_TRANSFER_SIZE
  59. | SpanMetricsField.MESSAGING_MESSAGE_RECEIVE_LATENCY
  60. | SpanMetricsField.CACHE_ITEM_SIZE;
  61. export type SpanStringFields =
  62. | 'span.op'
  63. | 'span.description'
  64. | 'span.module'
  65. | 'span.action'
  66. | 'span.group'
  67. | 'span.category'
  68. | 'transaction'
  69. | 'transaction.method'
  70. | 'release'
  71. | 'os.name'
  72. | 'span.status_code'
  73. | 'span.ai.pipeline.group'
  74. | 'project'
  75. | 'messaging.destination.name';
  76. export type SpanMetricsQueryFilters = {
  77. [Field in SpanStringFields]?: string;
  78. } & {
  79. [SpanMetricsField.PROJECT_ID]?: string;
  80. [SpanMetricsField.SPAN_DOMAIN]?: string;
  81. };
  82. export type SpanIndexedQueryFilters = {
  83. [Field in SpanStringFields]?: string;
  84. } & {
  85. [SpanIndexedField.PROJECT_ID]?: string;
  86. };
  87. export type SpanStringArrayFields = 'span.domain';
  88. export const COUNTER_AGGREGATES = ['sum', 'avg', 'min', 'max', 'p100'] as const;
  89. export const DISTRIBUTION_AGGREGATES = ['p50', 'p75', 'p95', 'p99'] as const;
  90. export const AGGREGATES = [...COUNTER_AGGREGATES, ...DISTRIBUTION_AGGREGATES] as const;
  91. export type Aggregate = (typeof AGGREGATES)[number];
  92. export type ConditionalAggregate =
  93. | `avg_if`
  94. | `count_op`
  95. | 'trace_status_rate'
  96. | 'time_spent_percentage';
  97. export const SPAN_FUNCTIONS = [
  98. 'sps',
  99. 'spm',
  100. 'count',
  101. 'time_spent_percentage',
  102. 'http_response_rate',
  103. 'http_error_count',
  104. 'cache_hit_rate',
  105. 'cache_miss_rate',
  106. 'sum',
  107. ] as const;
  108. const BREAKPOINT_CONDITIONS = ['less', 'greater'] as const;
  109. type BreakpointCondition = (typeof BREAKPOINT_CONDITIONS)[number];
  110. type RegressionFunctions = [
  111. `regression_score(${string},${string})`,
  112. `avg_by_timestamp(${string},${BreakpointCondition},${string})`,
  113. `epm_by_timestamp(${BreakpointCondition},${string})`,
  114. ][number];
  115. type SpanAnyFunction = `any(${string})`;
  116. export type SpanFunctions = (typeof SPAN_FUNCTIONS)[number];
  117. export type SpanMetricsResponse = {
  118. [Property in SpanNumberFields as `${Aggregate}(${Property})`]: number;
  119. } & {
  120. [Property in SpanFunctions as `${Property}()`]: number;
  121. } & {
  122. [Property in SpanStringFields as `${Property}`]: string;
  123. } & {
  124. [Property in SpanStringArrayFields as `${Property}`]: string[];
  125. } & {
  126. // TODO: This should include all valid HTTP codes or just all integers
  127. 'http_response_rate(2)': number;
  128. 'http_response_rate(3)': number;
  129. 'http_response_rate(4)': number;
  130. 'http_response_rate(5)': number;
  131. } & {
  132. ['project']: string;
  133. ['project.id']: number;
  134. } & {
  135. [Function in RegressionFunctions]: number;
  136. } & {
  137. [Function in SpanAnyFunction]: string;
  138. } & {
  139. [Property in ConditionalAggregate as
  140. | `${Property}(${string})`
  141. | `${Property}(${string},${string})`
  142. | `${Property}(${string},${string},${string})`]: number;
  143. };
  144. export type MetricsFilters = {
  145. [Property in SpanStringFields as `${Property}`]?: string | string[];
  146. };
  147. export type SpanMetricsProperty = keyof SpanMetricsResponse;
  148. export enum SpanIndexedField {
  149. ENVIRONMENT = 'environment',
  150. RESOURCE_RENDER_BLOCKING_STATUS = 'resource.render_blocking_status',
  151. HTTP_RESPONSE_CONTENT_LENGTH = 'http.response_content_length',
  152. SPAN_CATEGORY = 'span.category',
  153. SPAN_DURATION = 'span.duration',
  154. SPAN_SELF_TIME = 'span.self_time',
  155. SPAN_GROUP = 'span.group', // Span group computed from the normalized description. Matches the group in the metrics data set
  156. SPAN_MODULE = 'span.module',
  157. SPAN_DESCRIPTION = 'span.description',
  158. SPAN_STATUS = 'span.status',
  159. SPAN_OP = 'span.op',
  160. ID = 'span_id',
  161. SPAN_ACTION = 'span.action',
  162. SPAN_AI_PIPELINE_GROUP = 'span.ai.pipeline.group',
  163. SDK_NAME = 'sdk.name',
  164. TRACE = 'trace',
  165. TRANSACTION_ID = 'transaction.id',
  166. TRANSACTION_METHOD = 'transaction.method',
  167. TRANSACTION_OP = 'transaction.op',
  168. SPAN_DOMAIN = 'span.domain',
  169. TIMESTAMP = 'timestamp',
  170. RAW_DOMAIN = 'raw_domain',
  171. PROJECT = 'project',
  172. PROJECT_ID = 'project_id',
  173. PROFILE_ID = 'profile_id',
  174. RELEASE = 'release',
  175. TRANSACTION = 'transaction',
  176. ORIGIN_TRANSACTION = 'origin.transaction',
  177. REPLAY_ID = 'replay.id',
  178. BROWSER_NAME = 'browser.name',
  179. USER = 'user',
  180. USER_ID = 'user.id',
  181. USER_EMAIL = 'user.email',
  182. USER_USERNAME = 'user.username',
  183. INP = 'measurements.inp',
  184. INP_SCORE = 'measurements.score.inp',
  185. INP_SCORE_WEIGHT = 'measurements.score.weight.inp',
  186. TOTAL_SCORE = 'measurements.score.total',
  187. RESPONSE_CODE = 'span.status_code',
  188. CACHE_HIT = 'cache.hit',
  189. CACHE_ITEM_SIZE = 'measurements.cache.item_size',
  190. TRACE_STATUS = 'trace.status',
  191. MESSAGING_MESSAGE_ID = 'messaging.message.id',
  192. MESSAGING_MESSAGE_BODY_SIZE = 'measurements.messaging.message.body.size',
  193. MESSAGING_MESSAGE_RECEIVE_LATENCY = 'measurements.messaging.message.receive.latency',
  194. MESSAGING_MESSAGE_RETRY_COUNT = 'measurements.messaging.message.retry.count',
  195. MESSAGING_MESSAGE_DESTINATION_NAME = 'messaging.destination.name',
  196. }
  197. export type SpanIndexedResponse = {
  198. [SpanIndexedField.ENVIRONMENT]: string;
  199. [SpanIndexedField.RELEASE]: string;
  200. [SpanIndexedField.SDK_NAME]: string;
  201. [SpanIndexedField.SPAN_CATEGORY]: string;
  202. [SpanIndexedField.SPAN_DURATION]: number;
  203. [SpanIndexedField.SPAN_SELF_TIME]: number;
  204. [SpanIndexedField.SPAN_GROUP]: string;
  205. [SpanIndexedField.SPAN_MODULE]: string;
  206. [SpanIndexedField.SPAN_DESCRIPTION]: string;
  207. [SpanIndexedField.SPAN_OP]: string;
  208. [SpanIndexedField.SPAN_AI_PIPELINE_GROUP]: string;
  209. [SpanIndexedField.SPAN_STATUS]:
  210. | 'ok'
  211. | 'cancelled'
  212. | 'unknown'
  213. | 'invalid_argument'
  214. | 'deadline_exceeded'
  215. | 'not_found'
  216. | 'already_exists'
  217. | 'permission_denied'
  218. | 'resource_exhausted'
  219. | 'failed_precondition'
  220. | 'aborted'
  221. | 'out_of_range'
  222. | 'unimplemented'
  223. | 'internal_error'
  224. | 'unavailable'
  225. | 'data_loss'
  226. | 'unauthenticated';
  227. [SpanIndexedField.ID]: string;
  228. [SpanIndexedField.SPAN_ACTION]: string;
  229. [SpanIndexedField.TRACE]: string;
  230. [SpanIndexedField.TRANSACTION]: string;
  231. [SpanIndexedField.TRANSACTION_ID]: string;
  232. [SpanIndexedField.TRANSACTION_METHOD]: string;
  233. [SpanIndexedField.TRANSACTION_OP]: string;
  234. [SpanIndexedField.SPAN_DOMAIN]: string[];
  235. [SpanIndexedField.RAW_DOMAIN]: string;
  236. [SpanIndexedField.TIMESTAMP]: string;
  237. [SpanIndexedField.PROJECT]: string;
  238. [SpanIndexedField.PROJECT_ID]: number;
  239. [SpanIndexedField.PROFILE_ID]: string;
  240. [SpanIndexedField.RESOURCE_RENDER_BLOCKING_STATUS]: '' | 'non-blocking' | 'blocking';
  241. [SpanIndexedField.HTTP_RESPONSE_CONTENT_LENGTH]: string;
  242. [SpanIndexedField.ORIGIN_TRANSACTION]: string;
  243. [SpanIndexedField.REPLAY_ID]: string;
  244. [SpanIndexedField.BROWSER_NAME]: string;
  245. [SpanIndexedField.USER]: string;
  246. [SpanIndexedField.USER_ID]: string;
  247. [SpanIndexedField.USER_EMAIL]: string;
  248. [SpanIndexedField.USER_USERNAME]: string;
  249. [SpanIndexedField.INP]: number;
  250. [SpanIndexedField.INP_SCORE]: number;
  251. [SpanIndexedField.INP_SCORE_WEIGHT]: number;
  252. [SpanIndexedField.TOTAL_SCORE]: number;
  253. [SpanIndexedField.RESPONSE_CODE]: string;
  254. [SpanIndexedField.CACHE_HIT]: '' | 'true' | 'false';
  255. [SpanIndexedField.CACHE_ITEM_SIZE]: number;
  256. [SpanIndexedField.TRACE_STATUS]: string;
  257. [SpanIndexedField.MESSAGING_MESSAGE_ID]: string;
  258. [SpanIndexedField.MESSAGING_MESSAGE_BODY_SIZE]: number;
  259. [SpanIndexedField.MESSAGING_MESSAGE_RECEIVE_LATENCY]: number;
  260. [SpanIndexedField.MESSAGING_MESSAGE_RETRY_COUNT]: number;
  261. [SpanIndexedField.MESSAGING_MESSAGE_DESTINATION_NAME]: string;
  262. };
  263. export type SpanIndexedPropery = keyof SpanIndexedResponse;
  264. // TODO: When convenient, remove this alias and use `IndexedResponse` everywhere
  265. export type SpanIndexedFieldTypes = SpanIndexedResponse;
  266. export type Op = SpanIndexedFieldTypes[SpanIndexedField.SPAN_OP];
  267. export enum SpanFunction {
  268. SPS = 'sps',
  269. SPM = 'spm',
  270. TIME_SPENT_PERCENTAGE = 'time_spent_percentage',
  271. HTTP_ERROR_COUNT = 'http_error_count',
  272. HTTP_RESPONSE_RATE = 'http_response_rate',
  273. CACHE_HIT_RATE = 'cache_hit_rate',
  274. CACHE_MISS_RATE = 'cache_miss_rate',
  275. COUNT_OP = 'count_op',
  276. TRACE_STATUS_RATE = 'trace_status_rate',
  277. }
  278. export const StarfishDatasetFields = {
  279. [DiscoverDatasets.SPANS_METRICS]: SpanIndexedField,
  280. [DiscoverDatasets.SPANS_INDEXED]: SpanIndexedField,
  281. };
  282. export const STARFISH_AGGREGATION_FIELDS: Record<
  283. SpanFunction,
  284. FieldDefinition & {defaultOutputType: AggregationOutputType}
  285. > = {
  286. [SpanFunction.SPS]: {
  287. desc: t('Spans per second'),
  288. kind: FieldKind.FUNCTION,
  289. defaultOutputType: 'number',
  290. valueType: FieldValueType.NUMBER,
  291. },
  292. [SpanFunction.SPM]: {
  293. desc: t('Spans per minute'),
  294. kind: FieldKind.FUNCTION,
  295. defaultOutputType: 'number',
  296. valueType: FieldValueType.NUMBER,
  297. },
  298. [SpanFunction.TIME_SPENT_PERCENTAGE]: {
  299. desc: t('Span time spent percentage'),
  300. defaultOutputType: 'percentage',
  301. kind: FieldKind.FUNCTION,
  302. valueType: FieldValueType.NUMBER,
  303. },
  304. [SpanFunction.HTTP_ERROR_COUNT]: {
  305. desc: t('Count of 5XX http errors'),
  306. defaultOutputType: 'integer',
  307. kind: FieldKind.FUNCTION,
  308. valueType: FieldValueType.NUMBER,
  309. },
  310. [SpanFunction.HTTP_RESPONSE_RATE]: {
  311. desc: t('Percentage of HTTP responses by code'),
  312. defaultOutputType: 'percentage',
  313. kind: FieldKind.FUNCTION,
  314. valueType: FieldValueType.NUMBER,
  315. },
  316. [SpanFunction.CACHE_HIT_RATE]: {
  317. desc: t('Percentage of cache hits'),
  318. defaultOutputType: 'percentage',
  319. kind: FieldKind.FUNCTION,
  320. valueType: FieldValueType.NUMBER,
  321. },
  322. [SpanFunction.CACHE_MISS_RATE]: {
  323. desc: t('Percentage of cache misses'),
  324. defaultOutputType: 'percentage',
  325. kind: FieldKind.FUNCTION,
  326. valueType: FieldValueType.NUMBER,
  327. },
  328. [SpanFunction.COUNT_OP]: {
  329. desc: t('Count of spans with matching operation'),
  330. defaultOutputType: 'integer',
  331. kind: FieldKind.FUNCTION,
  332. valueType: FieldValueType.NUMBER,
  333. },
  334. [SpanFunction.TRACE_STATUS_RATE]: {
  335. desc: t('Percentage of spans with matching trace status'),
  336. defaultOutputType: 'percentage',
  337. kind: FieldKind.FUNCTION,
  338. valueType: FieldValueType.NUMBER,
  339. },
  340. };
  341. // TODO - add more functions and fields, combine shared ones, etc
  342. export const METRICS_FUNCTIONS = ['count'] as const;
  343. export enum MetricsFields {
  344. TRANSACTION_DURATION = 'transaction.duration',
  345. TRANSACTION = 'transaction',
  346. }
  347. export type MetricsNumberFields = MetricsFields.TRANSACTION_DURATION;
  348. export type MetricsStringFields = MetricsFields.TRANSACTION;
  349. export type MetricsFunctions = (typeof METRICS_FUNCTIONS)[number];
  350. export type MetricsResponse = {
  351. [Property in MetricsNumberFields as `${Aggregate}(${Property})`]: number;
  352. } & {
  353. [Property in MetricsStringFields as `${Property}`]: string;
  354. };
  355. export type MetricsProperty = keyof MetricsResponse;
  356. export type MetricsQueryFilters = {
  357. [Field in MetricsStringFields]?: string;
  358. } & {
  359. [SpanIndexedField.PROJECT_ID]?: string;
  360. };