/* * 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. */ import { HrTime, ValueType } from '@opentelemetry/api'; import * as assert from 'assert'; import { AggregationTemporality, InstrumentType, DataPointType, MetricData, } from '../../src'; import { HistogramAccumulation, HistogramAggregator, } from '../../src/aggregator'; import { commonValues, defaultInstrumentDescriptor } from '../util'; describe('HistogramAggregator', () => { describe('createAccumulation', () => { it('no exceptions on createAccumulation', () => { const aggregator = new HistogramAggregator([1, 10, 100], true); const accumulation = aggregator.createAccumulation([0, 0]); assert.ok(accumulation instanceof HistogramAccumulation); }); }); describe('merge', () => { it('no exceptions', () => { const aggregator = new HistogramAggregator([1, 10, 100], true); const prev = aggregator.createAccumulation([0, 0]); prev.record(0); prev.record(1); const delta = aggregator.createAccumulation([1, 1]); delta.record(2); delta.record(11); const expected = aggregator.createAccumulation([0, 0]); // replay actions on prev expected.record(0); expected.record(1); // replay actions on delta expected.record(2); expected.record(11); assert.deepStrictEqual(aggregator.merge(prev, delta), expected); }); it('with only negatives', () => { const aggregator = new HistogramAggregator([1, 10, 100], true); const prev = aggregator.createAccumulation([0, 0]); prev.record(-10); prev.record(-20); const delta = aggregator.createAccumulation([1, 1]); delta.record(-5); delta.record(-30); assert.deepStrictEqual(aggregator.merge(prev, delta).toPointValue(), { buckets: { boundaries: [1, 10, 100], counts: [4, 0, 0, 0], }, count: 4, hasMinMax: true, max: -5, min: -30, sum: -65, }); }); it('with single bucket', function () { const aggregator = new HistogramAggregator([], true); const prev = aggregator.createAccumulation([0, 0]); prev.record(0); prev.record(1); const delta = aggregator.createAccumulation([1, 1]); delta.record(2); delta.record(11); const expected = new HistogramAccumulation([0, 0], [], true, { buckets: { boundaries: [], counts: [4], }, count: 4, sum: 14, hasMinMax: true, min: 0, max: 11, }); assert.deepStrictEqual(aggregator.merge(prev, delta), expected); }); }); describe('diff', () => { it('no exceptions', () => { const aggregator = new HistogramAggregator([1, 10, 100], true); const prev = aggregator.createAccumulation([0, 0]); prev.record(0); prev.record(1); const curr = aggregator.createAccumulation([1, 1]); // replay actions on prev curr.record(0); curr.record(1); // perform new actions curr.record(2); curr.record(11); const expected = new HistogramAccumulation([1, 1], [1, 10, 100], true, { buckets: { boundaries: [1, 10, 100], counts: [0, 1, 1, 0], }, count: 2, sum: 13, hasMinMax: false, min: Infinity, max: -Infinity, }); assert.deepStrictEqual(aggregator.diff(prev, curr), expected); }); it('with single bucket', function () { const aggregator = new HistogramAggregator([], true); const prev = aggregator.createAccumulation([0, 0]); prev.record(0); prev.record(1); const curr = aggregator.createAccumulation([1, 1]); // replay actions on prev curr.record(0); curr.record(1); // perform new actions curr.record(2); curr.record(11); const expected = new HistogramAccumulation([1, 1], [], true, { buckets: { boundaries: [], counts: [2], }, count: 2, sum: 13, hasMinMax: false, min: Infinity, max: -Infinity, }); assert.deepStrictEqual(aggregator.diff(prev, curr), expected); }); }); describe('toMetricData', () => { it('should transform to expected data with recordMinMax = true', () => { const aggregator = new HistogramAggregator([1, 10, 100], true); const startTime: HrTime = [0, 0]; const endTime: HrTime = [1, 1]; const accumulation = aggregator.createAccumulation(startTime); accumulation.record(0); accumulation.record(1); const expected: MetricData = { descriptor: defaultInstrumentDescriptor, aggregationTemporality: AggregationTemporality.CUMULATIVE, dataPointType: DataPointType.HISTOGRAM, dataPoints: [ { attributes: {}, startTime, endTime, value: { buckets: { boundaries: [1, 10, 100], counts: [2, 0, 0, 0], }, count: 2, sum: 1, min: 0, max: 1, }, }, ], }; assert.deepStrictEqual( aggregator.toMetricData( defaultInstrumentDescriptor, AggregationTemporality.CUMULATIVE, [[{}, accumulation]], endTime ), expected ); }); it('should transform to expected data with recordMinMax = false', () => { const aggregator = new HistogramAggregator([1, 10, 100], false); const startTime: HrTime = [0, 0]; const endTime: HrTime = [1, 1]; const accumulation = aggregator.createAccumulation(startTime); accumulation.record(0); accumulation.record(1); const expected: MetricData = { descriptor: defaultInstrumentDescriptor, aggregationTemporality: AggregationTemporality.CUMULATIVE, dataPointType: DataPointType.HISTOGRAM, dataPoints: [ { attributes: {}, startTime, endTime, value: { buckets: { boundaries: [1, 10, 100], counts: [2, 0, 0, 0], }, count: 2, sum: 1, min: undefined, max: undefined, }, }, ], }; assert.deepStrictEqual( aggregator.toMetricData( defaultInstrumentDescriptor, AggregationTemporality.CUMULATIVE, [[{}, accumulation]], endTime ), expected ); }); it('should transform to expected data with empty boundaries', () => { const aggregator = new HistogramAggregator([], false); const startTime: HrTime = [0, 0]; const endTime: HrTime = [1, 1]; const accumulation = aggregator.createAccumulation(startTime); accumulation.record(0); accumulation.record(1); const expected: MetricData = { descriptor: defaultInstrumentDescriptor, aggregationTemporality: AggregationTemporality.CUMULATIVE, dataPointType: DataPointType.HISTOGRAM, dataPoints: [ { attributes: {}, startTime, endTime, value: { buckets: { boundaries: [], counts: [2], }, count: 2, sum: 1, min: undefined, max: undefined, }, }, ], }; assert.deepStrictEqual( aggregator.toMetricData( defaultInstrumentDescriptor, AggregationTemporality.CUMULATIVE, [[{}, accumulation]], endTime ), expected ); }); function testSum(instrumentType: InstrumentType, expectSum: boolean) { const aggregator = new HistogramAggregator([1, 10, 100], true); const startTime: HrTime = [0, 0]; const endTime: HrTime = [1, 1]; const accumulation = aggregator.createAccumulation(startTime); accumulation.record(0); accumulation.record(1); accumulation.record(4); const aggregatedData = aggregator.toMetricData( { name: 'default_metric', description: 'a simple instrument', type: instrumentType, unit: '1', valueType: ValueType.DOUBLE, advice: {}, }, AggregationTemporality.CUMULATIVE, [[{}, accumulation]], endTime ); assert.notStrictEqual(aggregatedData, undefined); assert.strictEqual( aggregatedData?.dataPoints[0].value.sum, expectSum ? 5 : undefined ); } describe('should have undefined sum when used with', () => { it('UpDownCounter', () => testSum(InstrumentType.UP_DOWN_COUNTER, false)); it('ObservableUpDownCounter', () => testSum(InstrumentType.OBSERVABLE_UP_DOWN_COUNTER, false)); it('ObservableUpDownCounter', () => testSum(InstrumentType.OBSERVABLE_GAUGE, false)); }); describe('should include sum with', () => { it('UpDownCounter', () => testSum(InstrumentType.COUNTER, true)); it('ObservableUpDownCounter', () => testSum(InstrumentType.HISTOGRAM, true)); it('ObservableUpDownCounter', () => testSum(InstrumentType.OBSERVABLE_COUNTER, true)); }); }); }); describe('HistogramAccumulation', () => { describe('record', () => { it('no exceptions on record', () => { const accumulation = new HistogramAccumulation([0, 0], [1, 10, 100]); for (const value of commonValues) { accumulation.record(value); } }); it('ignores NaN', () => { const accumulation = new HistogramAccumulation([0, 0], [1, 10, 100]); accumulation.record(NaN); const pointValue = accumulation.toPointValue(); assert.strictEqual(pointValue.max, -Infinity); assert.strictEqual(pointValue.min, Infinity); assert.strictEqual(pointValue.sum, 0); assert.strictEqual(pointValue.count, 0); assert.deepStrictEqual(pointValue.buckets.counts, [0, 0, 0, 0]); }); }); describe('setStartTime', () => { it('should set start time', () => { const accumulation = new HistogramAccumulation([0, 0], [1, 10, 100]); accumulation.setStartTime([1, 1]); assert.deepStrictEqual(accumulation.startTime, [1, 1]); }); }); });