prom-client icon indicating copy to clipboard operation
prom-client copied to clipboard

[Feature] ExponentialBuckets with start and end

Open Janpot opened this issue 7 years ago • 2 comments

Usually when constructing exponential buckets I have a rough estimate in mind of what my boundaries are. I'd for instance think in terms of, start + end + count, rather than start + factor + count. And I find myself writing helper functions like:

function exponentialStartEndBuckets (start, end, count) {
  const factor = (end / start) ** (1 / (count - 1));
  return promClient.exponentialBuckets(start, factor, count);
}

I propose adding a helper function like this one (but probably better named).

Edit:

a more elaborate version that does some rounding

function roundToPrecision (num, precision = 0) { 
  // https://stackoverflow.com/a/18358056
  return Number(`${Math.round(`${num}e+${precision}`)}e-${precision}`);
}

function exponentialStartEndBuckets (start, end, count) {
  const factor = (end / start) ** (1 / (count - 1));
  const buckets = promClient.exponentialBuckets(start, factor, count);
  const precision = Math.max(0, -Math.floor(Math.log10(buckets[1] - buckets[0])));
  return buckets.map(bucket => roundToPrecision(bucket, precision));
}

or even

function roundToPrecision (num, precision = 0) { 
  // https://stackoverflow.com/a/18358056
  return Number(`${Math.round(`${num}e+${precision}`)}e-${precision}`);
}

function exponentialStartEndBuckets (start, end, count) {
  const factor = (end / start) ** (1 / (count - 1));
  const buckets = promClient.exponentialBuckets(start, factor, count);
  let currentBucket = null;
  return buckets.map(bucket => {
    if (currentBucket === null) {
      currentBucket = bucket;
    } else {
      const precision = Math.max(0, -Math.floor(Math.log10(bucket - currentBucket)));
      currentBucket = roundToPrecision(bucket, precision);
    }
    return currentBucket;
  });
}

Janpot avatar Sep 06 '18 13:09 Janpot

@siimon I can cook up a PR for this. Do you have a preference/suggestion for naming the function?

Another option could be to have exponentialBuckets accept an object as the first parameter so it can be called as

exponentialBuckets(1, 2, 10);

exponentialBuckets({
  start: 1,
  factor: 2,
  count: 10
});

exponentialBuckets({
  start: 1,
  end: 30,
  count: 10
});

Janpot avatar Oct 04 '18 09:10 Janpot

I think your suggestion is good, to have an object as first parameter. But if you implement it that way, please keep the backwards compatibility so we don't need to make a breaking change!

Thanks for your suggestion btw!

siimon avatar Oct 08 '18 20:10 siimon