formatters.tsx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. import {t} from 'sentry/locale';
  2. import type {MetricType} from 'sentry/types/metrics';
  3. import {defined} from 'sentry/utils';
  4. import {formatBytesBase2} from 'sentry/utils/bytes/formatBytesBase2';
  5. import {formatBytesBase10} from 'sentry/utils/bytes/formatBytesBase10';
  6. import {
  7. DAY,
  8. formatAbbreviatedNumberWithDynamicPrecision,
  9. HOUR,
  10. MICROSECOND,
  11. MILLISECOND,
  12. MINUTE,
  13. MONTH,
  14. NANOSECOND,
  15. SECOND,
  16. WEEK,
  17. } from 'sentry/utils/formatters';
  18. import {formatNumberWithDynamicDecimalPoints} from 'sentry/utils/number/formatNumberWithDynamicDecimalPoints';
  19. const metricTypeToReadable: Record<MetricType, string> = {
  20. c: t('counter'),
  21. g: t('gauge'),
  22. d: t('distribution'),
  23. s: t('set'),
  24. e: t('derived'),
  25. };
  26. // Converts from "c" to "counter"
  27. export function getReadableMetricType(type?: string) {
  28. return metricTypeToReadable[type as MetricType] ?? t('unknown');
  29. }
  30. function formatDuration(seconds: number): string {
  31. if (!seconds) {
  32. return '0ms';
  33. }
  34. const absValue = Math.abs(seconds * 1000);
  35. // value in milliseconds
  36. const msValue = seconds * 1000;
  37. let unit: FormattingSupportedMetricUnit | 'month' = 'nanosecond';
  38. let value = msValue / NANOSECOND;
  39. if (absValue >= MONTH) {
  40. unit = 'month';
  41. value = msValue / MONTH;
  42. } else if (absValue >= WEEK) {
  43. unit = 'week';
  44. value = msValue / WEEK;
  45. } else if (absValue >= DAY) {
  46. unit = 'day';
  47. value = msValue / DAY;
  48. } else if (absValue >= HOUR) {
  49. unit = 'hour';
  50. value = msValue / HOUR;
  51. } else if (absValue >= MINUTE) {
  52. unit = 'minute';
  53. value = msValue / MINUTE;
  54. } else if (absValue >= SECOND) {
  55. unit = 'second';
  56. value = msValue / SECOND;
  57. } else if (absValue >= MILLISECOND) {
  58. unit = 'millisecond';
  59. value = msValue;
  60. } else if (absValue >= MICROSECOND) {
  61. unit = 'microsecond';
  62. value = msValue / MICROSECOND;
  63. }
  64. return `${formatNumberWithDynamicDecimalPoints(value)}${
  65. unit === 'month' ? 'mo' : METRIC_UNIT_TO_SHORT[unit]
  66. }`;
  67. }
  68. // The metric units that we have support for in the UI
  69. // others will still be displayed, but will not have any effect on formatting
  70. export const formattingSupportedMetricUnits = [
  71. 'none',
  72. 'nanosecond',
  73. 'nanoseconds',
  74. 'microsecond',
  75. 'microseconds',
  76. 'millisecond',
  77. 'milliseconds',
  78. 'second',
  79. 'seconds',
  80. 'minute',
  81. 'minutes',
  82. 'hour',
  83. 'hours',
  84. 'day',
  85. 'days',
  86. 'week',
  87. 'weeks',
  88. 'ratio',
  89. 'percent',
  90. 'percents',
  91. 'bit',
  92. 'bits',
  93. 'byte',
  94. 'bytes',
  95. 'kibibyte',
  96. 'kibibytes',
  97. 'kilobyte',
  98. 'kilobytes',
  99. 'mebibyte',
  100. 'mebibytes',
  101. 'megabyte',
  102. 'megabytes',
  103. 'gibibyte',
  104. 'gibibytes',
  105. 'gigabyte',
  106. 'gigabytes',
  107. 'tebibyte',
  108. 'tebibytes',
  109. 'terabyte',
  110. 'terabytes',
  111. 'pebibyte',
  112. 'pebibytes',
  113. 'petabyte',
  114. 'petabytes',
  115. 'exbibyte',
  116. 'exbibytes',
  117. 'exabyte',
  118. 'exabytes',
  119. ] as const;
  120. export type FormattingSupportedMetricUnit =
  121. (typeof formattingSupportedMetricUnits)[number];
  122. export const formattingSupportedMetricUnitsSingular: FormattingSupportedMetricUnit[] = [
  123. 'none',
  124. 'nanosecond',
  125. 'microsecond',
  126. 'millisecond',
  127. 'second',
  128. 'minute',
  129. 'hour',
  130. 'day',
  131. 'week',
  132. 'ratio',
  133. 'percent',
  134. 'bit',
  135. 'byte',
  136. 'kibibyte',
  137. 'kilobyte',
  138. 'mebibyte',
  139. 'megabyte',
  140. 'gibibyte',
  141. 'gigabyte',
  142. 'tebibyte',
  143. 'terabyte',
  144. 'pebibyte',
  145. 'petabyte',
  146. 'exbibyte',
  147. 'exabyte',
  148. ];
  149. const METRIC_UNIT_TO_SHORT: Record<FormattingSupportedMetricUnit, string> = {
  150. nanosecond: 'ns',
  151. nanoseconds: 'ns',
  152. microsecond: 'μs',
  153. microseconds: 'μs',
  154. millisecond: 'ms',
  155. milliseconds: 'ms',
  156. second: 's',
  157. seconds: 's',
  158. minute: 'min',
  159. minutes: 'min',
  160. hour: 'hr',
  161. hours: 'hr',
  162. day: 'day',
  163. days: 'day',
  164. week: 'wk',
  165. weeks: 'wk',
  166. ratio: '%',
  167. percent: '%',
  168. percents: '%',
  169. bit: 'b',
  170. bits: 'b',
  171. byte: 'B',
  172. bytes: 'B',
  173. kibibyte: 'KiB',
  174. kibibytes: 'KiB',
  175. kilobyte: 'KB',
  176. kilobytes: 'KB',
  177. mebibyte: 'MiB',
  178. mebibytes: 'MiB',
  179. megabyte: 'MB',
  180. megabytes: 'MB',
  181. gibibyte: 'GiB',
  182. gibibytes: 'GiB',
  183. gigabyte: 'GB',
  184. gigabytes: 'GB',
  185. tebibyte: 'TiB',
  186. tebibytes: 'TiB',
  187. terabyte: 'TB',
  188. terabytes: 'TB',
  189. pebibyte: 'PiB',
  190. pebibytes: 'PiB',
  191. petabyte: 'PB',
  192. petabytes: 'PB',
  193. exbibyte: 'EiB',
  194. exbibytes: 'EiB',
  195. exabyte: 'EB',
  196. exabytes: 'EB',
  197. none: '',
  198. };
  199. export function formatMetricUsingUnit(value: number | null, unit: string) {
  200. if (!defined(value)) {
  201. return '\u2014';
  202. }
  203. switch (unit as FormattingSupportedMetricUnit) {
  204. case 'nanosecond':
  205. case 'nanoseconds':
  206. return formatDuration(value / 1000000000);
  207. case 'microsecond':
  208. case 'microseconds':
  209. return formatDuration(value / 1000000);
  210. case 'millisecond':
  211. case 'milliseconds':
  212. return formatDuration(value / 1000);
  213. case 'second':
  214. case 'seconds':
  215. return formatDuration(value);
  216. case 'minute':
  217. case 'minutes':
  218. return formatDuration(value * 60);
  219. case 'hour':
  220. case 'hours':
  221. return formatDuration(value * 60 * 60);
  222. case 'day':
  223. case 'days':
  224. return formatDuration(value * 60 * 60 * 24);
  225. case 'week':
  226. case 'weeks':
  227. return formatDuration(value * 60 * 60 * 24 * 7);
  228. case 'ratio':
  229. return `${formatNumberWithDynamicDecimalPoints(value * 100)}%`;
  230. case 'percent':
  231. case 'percents':
  232. return `${formatNumberWithDynamicDecimalPoints(value)}%`;
  233. case 'bit':
  234. case 'bits':
  235. return formatBytesBase2(value / 8);
  236. case 'byte':
  237. case 'bytes':
  238. return formatBytesBase10(value);
  239. // Only used internally to support normalized byte metrics while preserving base 2 formatting
  240. case 'byte2' as FormattingSupportedMetricUnit:
  241. return formatBytesBase2(value);
  242. case 'kibibyte':
  243. case 'kibibytes':
  244. return formatBytesBase2(value * 1024);
  245. case 'kilobyte':
  246. case 'kilobytes':
  247. return formatBytesBase10(value, 1);
  248. case 'mebibyte':
  249. case 'mebibytes':
  250. return formatBytesBase2(value * 1024 ** 2);
  251. case 'megabyte':
  252. case 'megabytes':
  253. return formatBytesBase10(value, 2);
  254. case 'gibibyte':
  255. case 'gibibytes':
  256. return formatBytesBase2(value * 1024 ** 3);
  257. case 'gigabyte':
  258. case 'gigabytes':
  259. return formatBytesBase10(value, 3);
  260. case 'tebibyte':
  261. case 'tebibytes':
  262. return formatBytesBase2(value * 1024 ** 4);
  263. case 'terabyte':
  264. case 'terabytes':
  265. return formatBytesBase10(value, 4);
  266. case 'pebibyte':
  267. case 'pebibytes':
  268. return formatBytesBase2(value * 1024 ** 5);
  269. case 'petabyte':
  270. case 'petabytes':
  271. return formatBytesBase10(value, 5);
  272. case 'exbibyte':
  273. case 'exbibytes':
  274. return formatBytesBase2(value * 1024 ** 6);
  275. case 'exabyte':
  276. case 'exabytes':
  277. return formatBytesBase10(value, 6);
  278. case 'none':
  279. default:
  280. return formatAbbreviatedNumberWithDynamicPrecision(value);
  281. }
  282. }
  283. const getShortMetricUnit = (unit: string): string => METRIC_UNIT_TO_SHORT[unit] ?? '';
  284. export function formatMetricUsingFixedUnit(
  285. value: number | null,
  286. unit: string,
  287. op?: string
  288. ) {
  289. if (value === null) {
  290. return '\u2014';
  291. }
  292. const formattedNumber = formatNumberWithDynamicDecimalPoints(value);
  293. return op === 'count'
  294. ? formattedNumber
  295. : `${formattedNumber}${getShortMetricUnit(unit)}`.trim();
  296. }