/* * Copyright The OpenTelemetry Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; import { AggregatorKind, } from './types'; import { DataPointType, } from '../export/MetricData'; import { diag } from '@opentelemetry/api'; import { InstrumentType } from '../InstrumentDescriptor'; import { Buckets } from './exponential-histogram/Buckets'; import { getMapping } from './exponential-histogram/mapping/getMapping'; import { nextGreaterSquare } from './exponential-histogram/util'; // HighLow is a utility class used for computing a common scale for // two exponential histogram accumulations var HighLow = /** @class */ (function () { function HighLow(low, high) { this.low = low; this.high = high; } HighLow.combine = function (h1, h2) { return new HighLow(Math.min(h1.low, h2.low), Math.max(h1.high, h2.high)); }; return HighLow; }()); var MAX_SCALE = 20; var DEFAULT_MAX_SIZE = 160; var MIN_MAX_SIZE = 2; var ExponentialHistogramAccumulation = /** @class */ (function () { function ExponentialHistogramAccumulation(startTime, _maxSize, _recordMinMax, _sum, _count, _zeroCount, _min, _max, _positive, _negative, _mapping) { if (startTime === void 0) { startTime = startTime; } if (_maxSize === void 0) { _maxSize = DEFAULT_MAX_SIZE; } if (_recordMinMax === void 0) { _recordMinMax = true; } if (_sum === void 0) { _sum = 0; } if (_count === void 0) { _count = 0; } if (_zeroCount === void 0) { _zeroCount = 0; } if (_min === void 0) { _min = Number.POSITIVE_INFINITY; } if (_max === void 0) { _max = Number.NEGATIVE_INFINITY; } if (_positive === void 0) { _positive = new Buckets(); } if (_negative === void 0) { _negative = new Buckets(); } if (_mapping === void 0) { _mapping = getMapping(MAX_SCALE); } this.startTime = startTime; this._maxSize = _maxSize; this._recordMinMax = _recordMinMax; this._sum = _sum; this._count = _count; this._zeroCount = _zeroCount; this._min = _min; this._max = _max; this._positive = _positive; this._negative = _negative; this._mapping = _mapping; if (this._maxSize < MIN_MAX_SIZE) { diag.warn("Exponential Histogram Max Size set to " + this._maxSize + ", changing to the minimum size of: " + MIN_MAX_SIZE); this._maxSize = MIN_MAX_SIZE; } } /** * record updates a histogram with a single count * @param {Number} value */ ExponentialHistogramAccumulation.prototype.record = function (value) { this.updateByIncrement(value, 1); }; /** * Sets the start time for this accumulation * @param {HrTime} startTime */ ExponentialHistogramAccumulation.prototype.setStartTime = function (startTime) { this.startTime = startTime; }; /** * Returns the datapoint representation of this accumulation * @param {HrTime} startTime */ ExponentialHistogramAccumulation.prototype.toPointValue = function () { return { hasMinMax: this._recordMinMax, min: this.min, max: this.max, sum: this.sum, positive: { offset: this.positive.offset, bucketCounts: this.positive.counts(), }, negative: { offset: this.negative.offset, bucketCounts: this.negative.counts(), }, count: this.count, scale: this.scale, zeroCount: this.zeroCount, }; }; Object.defineProperty(ExponentialHistogramAccumulation.prototype, "sum", { /** * @returns {Number} The sum of values recorded by this accumulation */ get: function () { return this._sum; }, enumerable: false, configurable: true }); Object.defineProperty(ExponentialHistogramAccumulation.prototype, "min", { /** * @returns {Number} The minimum value recorded by this accumulation */ get: function () { return this._min; }, enumerable: false, configurable: true }); Object.defineProperty(ExponentialHistogramAccumulation.prototype, "max", { /** * @returns {Number} The maximum value recorded by this accumulation */ get: function () { return this._max; }, enumerable: false, configurable: true }); Object.defineProperty(ExponentialHistogramAccumulation.prototype, "count", { /** * @returns {Number} The count of values recorded by this accumulation */ get: function () { return this._count; }, enumerable: false, configurable: true }); Object.defineProperty(ExponentialHistogramAccumulation.prototype, "zeroCount", { /** * @returns {Number} The number of 0 values recorded by this accumulation */ get: function () { return this._zeroCount; }, enumerable: false, configurable: true }); Object.defineProperty(ExponentialHistogramAccumulation.prototype, "scale", { /** * @returns {Number} The scale used by this accumulation */ get: function () { if (this._count === this._zeroCount) { // all zeros! scale doesn't matter, use zero return 0; } return this._mapping.scale; }, enumerable: false, configurable: true }); Object.defineProperty(ExponentialHistogramAccumulation.prototype, "positive", { /** * positive holds the positive values * @returns {Buckets} */ get: function () { return this._positive; }, enumerable: false, configurable: true }); Object.defineProperty(ExponentialHistogramAccumulation.prototype, "negative", { /** * negative holds the negative values by their absolute value * @returns {Buckets} */ get: function () { return this._negative; }, enumerable: false, configurable: true }); /** * updateByIncr supports updating a histogram with a non-negative * increment. * @param value * @param increment */ ExponentialHistogramAccumulation.prototype.updateByIncrement = function (value, increment) { // NaN does not fall into any bucket, is not zero and should not be counted, // NaN is never greater than max nor less than min, therefore return as there's nothing for us to do. if (Number.isNaN(value)) { return; } if (value > this._max) { this._max = value; } if (value < this._min) { this._min = value; } this._count += increment; if (value === 0) { this._zeroCount += increment; return; } this._sum += value * increment; if (value > 0) { this._updateBuckets(this._positive, value, increment); } else { this._updateBuckets(this._negative, -value, increment); } }; /** * merge combines data from previous value into self * @param {ExponentialHistogramAccumulation} previous */ ExponentialHistogramAccumulation.prototype.merge = function (previous) { if (this._count === 0) { this._min = previous.min; this._max = previous.max; } else if (previous.count !== 0) { if (previous.min < this.min) { this._min = previous.min; } if (previous.max > this.max) { this._max = previous.max; } } this.startTime = previous.startTime; this._sum += previous.sum; this._count += previous.count; this._zeroCount += previous.zeroCount; var minScale = this._minScale(previous); this._downscale(this.scale - minScale); this._mergeBuckets(this.positive, previous, previous.positive, minScale); this._mergeBuckets(this.negative, previous, previous.negative, minScale); }; /** * diff subtracts other from self * @param {ExponentialHistogramAccumulation} other */ ExponentialHistogramAccumulation.prototype.diff = function (other) { this._min = Infinity; this._max = -Infinity; this._sum -= other.sum; this._count -= other.count; this._zeroCount -= other.zeroCount; var minScale = this._minScale(other); this._downscale(this.scale - minScale); this._diffBuckets(this.positive, other, other.positive, minScale); this._diffBuckets(this.negative, other, other.negative, minScale); }; /** * clone returns a deep copy of self * @returns {ExponentialHistogramAccumulation} */ ExponentialHistogramAccumulation.prototype.clone = function () { return new ExponentialHistogramAccumulation(this.startTime, this._maxSize, this._recordMinMax, this._sum, this._count, this._zeroCount, this._min, this._max, this.positive.clone(), this.negative.clone(), this._mapping); }; /** * _updateBuckets maps the incoming value to a bucket index for the current * scale. If the bucket index is outside of the range of the backing array, * it will rescale the backing array and update the mapping for the new scale. */ ExponentialHistogramAccumulation.prototype._updateBuckets = function (buckets, value, increment) { var index = this._mapping.mapToIndex(value); // rescale the mapping if needed var rescalingNeeded = false; var high = 0; var low = 0; if (buckets.length === 0) { buckets.indexStart = index; buckets.indexEnd = buckets.indexStart; buckets.indexBase = buckets.indexStart; } else if (index < buckets.indexStart && buckets.indexEnd - index >= this._maxSize) { rescalingNeeded = true; low = index; high = buckets.indexEnd; } else if (index > buckets.indexEnd && index - buckets.indexStart >= this._maxSize) { rescalingNeeded = true; low = buckets.indexStart; high = index; } // rescale and compute index at new scale if (rescalingNeeded) { var change = this._changeScale(high, low); this._downscale(change); index = this._mapping.mapToIndex(value); } this._incrementIndexBy(buckets, index, increment); }; /** * _incrementIndexBy increments the count of the bucket specified by `index`. * If the index is outside of the range [buckets.indexStart, buckets.indexEnd] * the boundaries of the backing array will be adjusted and more buckets will * be added if needed. */ ExponentialHistogramAccumulation.prototype._incrementIndexBy = function (buckets, index, increment) { if (increment === 0) { // nothing to do for a zero increment, can happen during a merge operation return; } if (buckets.length === 0) { buckets.indexStart = buckets.indexEnd = buckets.indexBase = index; } if (index < buckets.indexStart) { var span = buckets.indexEnd - index; if (span >= buckets.backing.length) { this._grow(buckets, span + 1); } buckets.indexStart = index; } else if (index > buckets.indexEnd) { var span = index - buckets.indexStart; if (span >= buckets.backing.length) { this._grow(buckets, span + 1); } buckets.indexEnd = index; } var bucketIndex = index - buckets.indexBase; if (bucketIndex < 0) { bucketIndex += buckets.backing.length; } buckets.incrementBucket(bucketIndex, increment); }; /** * grow resizes the backing array by doubling in size up to maxSize. * This extends the array with a bunch of zeros and copies the * existing counts to the same position. */ ExponentialHistogramAccumulation.prototype._grow = function (buckets, needed) { var size = buckets.backing.length; var bias = buckets.indexBase - buckets.indexStart; var oldPositiveLimit = size - bias; var newSize = nextGreaterSquare(needed); if (newSize > this._maxSize) { newSize = this._maxSize; } var newPositiveLimit = newSize - bias; buckets.backing.growTo(newSize, oldPositiveLimit, newPositiveLimit); }; /** * _changeScale computes how much downscaling is needed by shifting the * high and low values until they are separated by no more than size. */ ExponentialHistogramAccumulation.prototype._changeScale = function (high, low) { var change = 0; while (high - low >= this._maxSize) { high >>= 1; low >>= 1; change++; } return change; }; /** * _downscale subtracts `change` from the current mapping scale. */ ExponentialHistogramAccumulation.prototype._downscale = function (change) { if (change === 0) { return; } if (change < 0) { // Note: this should be impossible. If we get here it's because // there is a bug in the implementation. throw new Error("impossible change of scale: " + this.scale); } var newScale = this._mapping.scale - change; this._positive.downscale(change); this._negative.downscale(change); this._mapping = getMapping(newScale); }; /** * _minScale is used by diff and merge to compute an ideal combined scale */ ExponentialHistogramAccumulation.prototype._minScale = function (other) { var minScale = Math.min(this.scale, other.scale); var highLowPos = HighLow.combine(this._highLowAtScale(this.positive, this.scale, minScale), this._highLowAtScale(other.positive, other.scale, minScale)); var highLowNeg = HighLow.combine(this._highLowAtScale(this.negative, this.scale, minScale), this._highLowAtScale(other.negative, other.scale, minScale)); return Math.min(minScale - this._changeScale(highLowPos.high, highLowPos.low), minScale - this._changeScale(highLowNeg.high, highLowNeg.low)); }; /** * _highLowAtScale is used by diff and merge to compute an ideal combined scale. */ ExponentialHistogramAccumulation.prototype._highLowAtScale = function (buckets, currentScale, newScale) { if (buckets.length === 0) { return new HighLow(0, -1); } var shift = currentScale - newScale; return new HighLow(buckets.indexStart >> shift, buckets.indexEnd >> shift); }; /** * _mergeBuckets translates index values from another histogram and * adds the values into the corresponding buckets of this histogram. */ ExponentialHistogramAccumulation.prototype._mergeBuckets = function (ours, other, theirs, scale) { var theirOffset = theirs.offset; var theirChange = other.scale - scale; for (var i = 0; i < theirs.length; i++) { this._incrementIndexBy(ours, (theirOffset + i) >> theirChange, theirs.at(i)); } }; /** * _diffBuckets translates index values from another histogram and * subtracts the values in the corresponding buckets of this histogram. */ ExponentialHistogramAccumulation.prototype._diffBuckets = function (ours, other, theirs, scale) { var theirOffset = theirs.offset; var theirChange = other.scale - scale; for (var i = 0; i < theirs.length; i++) { var ourIndex = (theirOffset + i) >> theirChange; var bucketIndex = ourIndex - ours.indexBase; if (bucketIndex < 0) { bucketIndex += ours.backing.length; } ours.decrementBucket(bucketIndex, theirs.at(i)); } ours.trim(); }; return ExponentialHistogramAccumulation; }()); export { ExponentialHistogramAccumulation }; /** * Aggregator for ExponentialHistogramAccumulations */ var ExponentialHistogramAggregator = /** @class */ (function () { /** * @param _maxSize Maximum number of buckets for each of the positive * and negative ranges, exclusive of the zero-bucket. * @param _recordMinMax If set to true, min and max will be recorded. * Otherwise, min and max will not be recorded. */ function ExponentialHistogramAggregator(_maxSize, _recordMinMax) { this._maxSize = _maxSize; this._recordMinMax = _recordMinMax; this.kind = AggregatorKind.EXPONENTIAL_HISTOGRAM; } ExponentialHistogramAggregator.prototype.createAccumulation = function (startTime) { return new ExponentialHistogramAccumulation(startTime, this._maxSize, this._recordMinMax); }; /** * Return the result of the merge of two exponential histogram accumulations. */ ExponentialHistogramAggregator.prototype.merge = function (previous, delta) { var result = delta.clone(); result.merge(previous); return result; }; /** * Returns a new DELTA aggregation by comparing two cumulative measurements. */ ExponentialHistogramAggregator.prototype.diff = function (previous, current) { var result = current.clone(); result.diff(previous); return result; }; ExponentialHistogramAggregator.prototype.toMetricData = function (descriptor, aggregationTemporality, accumulationByAttributes, endTime) { return { descriptor: descriptor, aggregationTemporality: aggregationTemporality, dataPointType: DataPointType.EXPONENTIAL_HISTOGRAM, dataPoints: accumulationByAttributes.map(function (_a) { var _b = __read(_a, 2), attributes = _b[0], accumulation = _b[1]; var pointValue = accumulation.toPointValue(); // determine if instrument allows negative values. var allowsNegativeValues = descriptor.type === InstrumentType.GAUGE || descriptor.type === InstrumentType.UP_DOWN_COUNTER || descriptor.type === InstrumentType.OBSERVABLE_GAUGE || descriptor.type === InstrumentType.OBSERVABLE_UP_DOWN_COUNTER; return { attributes: attributes, startTime: accumulation.startTime, endTime: endTime, value: { min: pointValue.hasMinMax ? pointValue.min : undefined, max: pointValue.hasMinMax ? pointValue.max : undefined, sum: !allowsNegativeValues ? pointValue.sum : undefined, positive: { offset: pointValue.positive.offset, bucketCounts: pointValue.positive.bucketCounts, }, negative: { offset: pointValue.negative.offset, bucketCounts: pointValue.negative.bucketCounts, }, count: pointValue.count, scale: pointValue.scale, zeroCount: pointValue.zeroCount, }, }; }), }; }; return ExponentialHistogramAggregator; }()); export { ExponentialHistogramAggregator }; //# sourceMappingURL=ExponentialHistogram.js.map