utils.ts 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import {DURATION_UNITS} from 'sentry/utils/discover/fieldRenderers';
  2. import type {DiscoverDatasets} from 'sentry/utils/discover/types';
  3. import getDuration from 'sentry/utils/duration/getDuration';
  4. import {formatPercentage} from 'sentry/utils/number/formatPercentage';
  5. import {VitalState} from 'sentry/views/performance/vitalDetail/utils';
  6. const formatMetricValue = (metric: MetricValue, field?: string | undefined): string => {
  7. if (metric.value === undefined) {
  8. return '-';
  9. }
  10. if (typeof metric.value === 'number' && metric.type === 'duration' && metric.unit) {
  11. const seconds =
  12. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  13. (metric.value * ((metric.unit && DURATION_UNITS[metric.unit]) ?? 1)) / 1000;
  14. return getDuration(seconds, 2, true);
  15. }
  16. if (typeof metric.value === 'number' && metric.type === 'number') {
  17. if (isFinite(metric.value)) {
  18. return metric.value.toFixed(2);
  19. }
  20. return '-';
  21. }
  22. if (
  23. field === 'division(mobile.slow_frames,mobile.total_frames)' ||
  24. field === 'division(mobile.frozen_frames,mobile.total_frames)'
  25. ) {
  26. if (typeof metric.value === 'number' && isFinite(metric.value)) {
  27. return formatPercentage(metric.value, 2, {minimumValue: 0.0001});
  28. }
  29. return '-';
  30. }
  31. return String(metric.value);
  32. };
  33. // maps to PERFORMANCE_SCORE_COLORS keys
  34. export enum PerformanceScore {
  35. GOOD = 'good',
  36. NEEDS_IMPROVEMENT = 'needsImprovement',
  37. BAD = 'bad',
  38. NONE = 'none',
  39. }
  40. export type VitalStatus = {
  41. description: string | undefined;
  42. formattedValue: string | undefined;
  43. score: PerformanceScore;
  44. value: MetricValue | undefined;
  45. };
  46. export type VitalItem = {
  47. dataset: DiscoverDatasets;
  48. description: string;
  49. docs: React.ReactNode;
  50. field: string;
  51. getStatus: (value: MetricValue, field?: string | undefined) => VitalStatus;
  52. platformDocLinks: Record<string, string>;
  53. sdkDocLinks: Record<string, string>;
  54. setup: React.ReactNode | undefined;
  55. title: string;
  56. };
  57. export type MetricValue = {
  58. // the field type if defined, e.g. duration
  59. type: string | undefined;
  60. // the unit of the value, e.g. milliseconds
  61. unit: string | undefined;
  62. // the actual value
  63. value: string | number | undefined;
  64. };
  65. export const STATUS_UNKNOWN: VitalStatus = {
  66. description: undefined,
  67. formattedValue: undefined,
  68. value: undefined,
  69. score: PerformanceScore.NONE,
  70. };
  71. export function getColdAppStartPerformance(metric: MetricValue): VitalStatus {
  72. let description = '';
  73. let status = PerformanceScore.NONE;
  74. if (typeof metric.value === 'number' && metric.unit) {
  75. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  76. const durationMs = metric.value * DURATION_UNITS[metric.unit];
  77. // TODO should be platform dependant
  78. if (durationMs > 5000) {
  79. status = PerformanceScore.BAD;
  80. description = VitalState.POOR;
  81. } else if (durationMs > 3000) {
  82. status = PerformanceScore.NEEDS_IMPROVEMENT;
  83. description = VitalState.MEH;
  84. } else if (durationMs > 0) {
  85. status = PerformanceScore.GOOD;
  86. description = VitalState.GOOD;
  87. }
  88. }
  89. return {
  90. value: metric,
  91. formattedValue: formatMetricValue(metric),
  92. score: status,
  93. description,
  94. };
  95. }
  96. export function getWarmAppStartPerformance(metric: MetricValue): VitalStatus {
  97. let description = '';
  98. let status = PerformanceScore.NONE;
  99. if (typeof metric.value === 'number' && metric.unit) {
  100. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  101. const durationMs = metric.value * DURATION_UNITS[metric.unit];
  102. // TODO should be platform dependant
  103. if (durationMs > 2000) {
  104. status = PerformanceScore.BAD;
  105. description = VitalState.POOR;
  106. } else if (durationMs > 1000) {
  107. status = PerformanceScore.NEEDS_IMPROVEMENT;
  108. description = VitalState.MEH;
  109. } else if (durationMs > 0) {
  110. status = PerformanceScore.GOOD;
  111. description = VitalState.GOOD;
  112. }
  113. }
  114. return {
  115. value: metric,
  116. formattedValue: formatMetricValue(metric),
  117. score: status,
  118. description,
  119. };
  120. }
  121. export function getDefaultMetricPerformance(
  122. metric: MetricValue,
  123. field?: string | undefined
  124. ): VitalStatus {
  125. return {
  126. description: undefined,
  127. formattedValue: formatMetricValue(metric, field),
  128. value: metric,
  129. score: PerformanceScore.NONE,
  130. };
  131. }