Files
2024-02-15 17:01:50 +00:00

291 lines
6.6 KiB
TypeScript

import {t} from 'sentry/locale';
import type {MetricType} from 'sentry/types/metrics';
import {defined, formatBytesBase2, formatBytesBase10} from 'sentry/utils';
import {
DAY,
formatNumberWithDynamicDecimalPoints,
HOUR,
MINUTE,
MONTH,
SECOND,
WEEK,
} from 'sentry/utils/formatters';
const metricTypeToReadable: Record<MetricType, string> = {
c: t('counter'),
g: t('gauge'),
d: t('distribution'),
s: t('set'),
e: t('derived'),
};
// Converts from "c" to "counter"
export function getReadableMetricType(type?: string) {
return metricTypeToReadable[type as MetricType] ?? t('unknown');
}
const MILLISECOND = 1;
const MICROSECOND = MILLISECOND / 1000;
const NANOSECOND = MICROSECOND / 1000;
function formatDuration(seconds: number): string {
if (!seconds) {
return '0ms';
}
const absValue = Math.abs(seconds * 1000);
// value in milliseconds
const msValue = seconds * 1000;
let unit: FormattingSupportedMetricUnit | 'month' = 'nanosecond';
let value = msValue / NANOSECOND;
if (absValue >= MONTH) {
unit = 'month';
value = msValue / MONTH;
} else if (absValue >= WEEK) {
unit = 'week';
value = msValue / WEEK;
} else if (absValue >= DAY) {
unit = 'day';
value = msValue / DAY;
} else if (absValue >= HOUR) {
unit = 'hour';
value = msValue / HOUR;
} else if (absValue >= MINUTE) {
unit = 'minute';
value = msValue / MINUTE;
} else if (absValue >= SECOND) {
unit = 'second';
value = msValue / SECOND;
} else if (absValue >= MILLISECOND) {
unit = 'millisecond';
value = msValue;
} else if (absValue >= MICROSECOND) {
unit = 'microsecond';
value = msValue / MICROSECOND;
}
return `${formatNumberWithDynamicDecimalPoints(value)}${
unit === 'month' ? 'mo' : METRIC_UNIT_TO_SHORT[unit]
}`;
}
// The metric units that we have support for in the UI
// others will still be displayed, but will not have any effect on formatting
export const formattingSupportedMetricUnits = [
'none',
'nanosecond',
'nanoseconds',
'microsecond',
'microseconds',
'millisecond',
'milliseconds',
'second',
'seconds',
'minute',
'minutes',
'hour',
'hours',
'day',
'days',
'week',
'weeks',
'ratio',
'percent',
'percents',
'bit',
'bits',
'byte',
'bytes',
'kibibyte',
'kibibytes',
'kilobyte',
'kilobytes',
'mebibyte',
'mebibytes',
'megabyte',
'megabytes',
'gibibyte',
'gibibytes',
'gigabyte',
'gigabytes',
'tebibyte',
'tebibytes',
'terabyte',
'terabytes',
'pebibyte',
'pebibytes',
'petabyte',
'petabytes',
'exbibyte',
'exbibytes',
'exabyte',
'exabytes',
] as const;
type FormattingSupportedMetricUnit = (typeof formattingSupportedMetricUnits)[number];
const METRIC_UNIT_TO_SHORT: Record<FormattingSupportedMetricUnit, string> = {
nanosecond: 'ns',
nanoseconds: 'ns',
microsecond: 'μs',
microseconds: 'μs',
millisecond: 'ms',
milliseconds: 'ms',
second: 's',
seconds: 's',
minute: 'min',
minutes: 'min',
hour: 'hr',
hours: 'hr',
day: 'day',
days: 'day',
week: 'wk',
weeks: 'wk',
ratio: '%',
percent: '%',
percents: '%',
bit: 'b',
bits: 'b',
byte: 'B',
bytes: 'B',
kibibyte: 'KiB',
kibibytes: 'KiB',
kilobyte: 'KB',
kilobytes: 'KB',
mebibyte: 'MiB',
mebibytes: 'MiB',
megabyte: 'MB',
megabytes: 'MB',
gibibyte: 'GiB',
gibibytes: 'GiB',
gigabyte: 'GB',
gigabytes: 'GB',
tebibyte: 'TiB',
tebibytes: 'TiB',
terabyte: 'TB',
terabytes: 'TB',
pebibyte: 'PiB',
pebibytes: 'PiB',
petabyte: 'PB',
petabytes: 'PB',
exbibyte: 'EiB',
exbibytes: 'EiB',
exabyte: 'EB',
exabytes: 'EB',
none: '',
};
export function formatMetricUsingUnit(value: number | null, unit: string) {
if (!defined(value)) {
return '\u2014';
}
switch (unit as FormattingSupportedMetricUnit) {
case 'nanosecond':
case 'nanoseconds':
return formatDuration(value / 1000000000);
case 'microsecond':
case 'microseconds':
return formatDuration(value / 1000000);
case 'millisecond':
case 'milliseconds':
return formatDuration(value / 1000);
case 'second':
case 'seconds':
return formatDuration(value);
case 'minute':
case 'minutes':
return formatDuration(value * 60);
case 'hour':
case 'hours':
return formatDuration(value * 60 * 60);
case 'day':
case 'days':
return formatDuration(value * 60 * 60 * 24);
case 'week':
case 'weeks':
return formatDuration(value * 60 * 60 * 24 * 7);
case 'ratio':
return `${formatNumberWithDynamicDecimalPoints(value * 100)}%`;
case 'percent':
case 'percents':
return `${formatNumberWithDynamicDecimalPoints(value)}%`;
case 'bit':
case 'bits':
return formatBytesBase2(value / 8);
case 'byte':
case 'bytes':
return formatBytesBase10(value);
case 'kibibyte':
case 'kibibytes':
return formatBytesBase2(value * 1024);
case 'kilobyte':
case 'kilobytes':
return formatBytesBase10(value, 1);
case 'mebibyte':
case 'mebibytes':
return formatBytesBase2(value * 1024 ** 2);
case 'megabyte':
case 'megabytes':
return formatBytesBase10(value, 2);
case 'gibibyte':
case 'gibibytes':
return formatBytesBase2(value * 1024 ** 3);
case 'gigabyte':
case 'gigabytes':
return formatBytesBase10(value, 3);
case 'tebibyte':
case 'tebibytes':
return formatBytesBase2(value * 1024 ** 4);
case 'terabyte':
case 'terabytes':
return formatBytesBase10(value, 4);
case 'pebibyte':
case 'pebibytes':
return formatBytesBase2(value * 1024 ** 5);
case 'petabyte':
case 'petabytes':
return formatBytesBase10(value, 5);
case 'exbibyte':
case 'exbibytes':
return formatBytesBase2(value * 1024 ** 6);
case 'exabyte':
case 'exabytes':
return formatBytesBase10(value, 6);
case 'none':
default:
return value.toLocaleString();
}
}
const getShortMetricUnit = (unit: string): string => METRIC_UNIT_TO_SHORT[unit] ?? '';
export function formatMetricUsingFixedUnit(
value: number | null,
unit: string,
op?: string
) {
if (value === null) {
return '\u2014';
}
const formattedNumber = formatNumberWithDynamicDecimalPoints(value);
return op === 'count'
? formattedNumber
: `${formattedNumber}${getShortMetricUnit(unit)}`.trim();
}
export function formatMetricsUsingUnitAndOp(
value: number | null,
unit: string,
operation?: string
) {
if (operation === 'count') {
// if the operation is count, we want to ignore the unit and always format the value as a number
return value?.toLocaleString() ?? '';
}
return formatMetricUsingUnit(value, unit);
}