prometheus/web/ui/mantine-ui/src/pages/query/HistogramHelpers.ts

145 lines
4.4 KiB
TypeScript
Raw Normal View History

// Calculates a default width of exponential histogram bucket ranges. If the last bucket is [0, 0],
// the width is calculated using the second to last bucket. returns error if the last bucket is [-0, 0],
export function calculateDefaultExpBucketWidth(
last: [number, string, string, string],
buckets: [number, string, string, string][]
): number {
if (parseFloat(last[2]) === 0 || parseFloat(last[1]) === 0) {
if (buckets.length > 1) {
return Math.abs(
Math.log(Math.abs(parseFloat(buckets[buckets.length - 2][2]))) -
Math.log(Math.abs(parseFloat(buckets[buckets.length - 2][1])))
);
} else {
throw new Error(
"Only one bucket in histogram ([-0, 0]). Cannot calculate defaultExpBucketWidth."
);
}
} else {
return Math.abs(
Math.log(Math.abs(parseFloat(last[2]))) -
Math.log(Math.abs(parseFloat(last[1])))
);
}
}
// Finds the lowest positive value from the bucket ranges
// Returns 0 if no positive values are found or if there are no buckets.
export function findMinPositive(buckets: [number, string, string, string][]) {
if (!buckets || buckets.length === 0) {
return 0; // no buckets
}
for (let i = 0; i < buckets.length; i++) {
const right = parseFloat(buckets[i][2]);
const left = parseFloat(buckets[i][1]);
if (left > 0) {
return left;
}
if (left < 0 && right > 0) {
return right;
}
if (i === buckets.length - 1) {
if (right > 0) {
return right;
}
}
}
return 0; // all buckets are negative
}
// Finds the lowest negative value from the bucket ranges
// Returns 0 if no negative values are found or if there are no buckets.
export function findMaxNegative(buckets: [number, string, string, string][]) {
if (!buckets || buckets.length === 0) {
return 0; // no buckets
}
for (let i = 0; i < buckets.length; i++) {
const right = parseFloat(buckets[i][2]);
const left = parseFloat(buckets[i][1]);
const prevRight = i > 0 ? parseFloat(buckets[i - 1][2]) : 0;
if (right >= 0) {
if (i === 0) {
if (left < 0) {
return left; // return the first negative bucket
}
return 0; // all buckets are positive
}
return prevRight; // return the last negative bucket
}
}
console.log("findmaxneg returning: ", buckets[buckets.length - 1][2]);
return parseFloat(buckets[buckets.length - 1][2]); // all buckets are negative
}
// Calculates the left position of the zero axis as a percentage string.
export function findZeroAxisLeft(
scale: string,
rangeMin: number,
rangeMax: number,
minPositive: number,
maxNegative: number,
zeroBucketIdx: number,
widthNegative: number,
widthTotal: number,
expBucketWidth: number
): string {
if (scale === "linear") {
return ((0 - rangeMin) / (rangeMax - rangeMin)) * 100 + "%";
} else {
if (maxNegative === 0) {
return "0%";
}
if (minPositive === 0) {
return "100%";
}
if (zeroBucketIdx === -1) {
// if there is no zero bucket, we must zero axis between buckets around zero
return (widthNegative / widthTotal) * 100 + "%";
}
if ((widthNegative + 0.5 * expBucketWidth) / widthTotal > 0) {
return ((widthNegative + 0.5 * expBucketWidth) / widthTotal) * 100 + "%";
} else {
return "0%";
}
}
}
// Determines if the zero axis should be shown such that the zero label does not overlap with the range labels.
// The zero axis is shown if it is between 5% and 95% of the graph.
export function showZeroAxis(zeroAxisLeft: string) {
const axisNumber = parseFloat(zeroAxisLeft.slice(0, -1));
if (5 < axisNumber && axisNumber < 95) {
return true;
}
return false;
}
// Finds the index of the bucket whose range includes zero
export function findZeroBucket(
buckets: [number, string, string, string][]
): number {
for (let i = 0; i < buckets.length; i++) {
const left = parseFloat(buckets[i][1]);
const right = parseFloat(buckets[i][2]);
if (left <= 0 && right >= 0) {
return i;
}
}
return -1;
}
const leftDelim = (br: number): string => (br === 3 || br === 1 ? "[" : "(");
const rightDelim = (br: number): string => (br === 3 || br === 0 ? "]" : ")");
export const bucketRangeString = ([
boundaryRule,
leftBoundary,
rightBoundary,
_,
]: [number, string, string, string]): string => {
return `${leftDelim(boundaryRule)}${leftBoundary} -> ${rightBoundary}${rightDelim(boundaryRule)}`;
};