feat(metrics): prototype experimental advice support (#3876)
Co-authored-by: Marc Pichler <marc.pichler@dynatrace.com>
This commit is contained in:
parent
f8e187b473
commit
b6e532bf52
|
|
@ -11,6 +11,8 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/
|
|||
|
||||
### :rocket: (Enhancement)
|
||||
|
||||
* feat(metrics): prototype experimental advice support [#3876](https://github.com/open-telemetry/opentelemetry-js/pull/3876) @legendecas
|
||||
|
||||
### :bug: (Bug Fix)
|
||||
|
||||
### :books: (Refine Doc)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## Unreleased
|
||||
|
||||
### :rocket: (Enhancement)
|
||||
|
||||
* feat(metrics): prototype experimental advice support [#3876](https://github.com/open-telemetry/opentelemetry-js/pull/3876) @legendecas
|
||||
|
||||
## 1.6.0
|
||||
|
||||
### :bug: (Bug Fix)
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ export {
|
|||
ObservableUpDownCounter,
|
||||
UpDownCounter,
|
||||
BatchObservableCallback,
|
||||
MetricAdvice,
|
||||
MetricAttributes,
|
||||
MetricAttributeValue,
|
||||
ObservableCallback,
|
||||
|
|
|
|||
|
|
@ -18,6 +18,18 @@ import { Attributes, AttributeValue } from '../common/Attributes';
|
|||
import { Context } from '../context/types';
|
||||
import { BatchObservableResult, ObservableResult } from './ObservableResult';
|
||||
|
||||
/**
|
||||
* Advisory options influencing aggregation configuration parameters.
|
||||
* @experimental
|
||||
*/
|
||||
export interface MetricAdvice {
|
||||
/**
|
||||
* Hint the explicit bucket boundaries for SDK if the metric is been
|
||||
* aggregated with a HistogramAggregator.
|
||||
*/
|
||||
explicitBucketBoundaries?: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Options needed for metric creation
|
||||
*/
|
||||
|
|
@ -39,6 +51,12 @@ export interface MetricOptions {
|
|||
* @default {@link ValueType.DOUBLE}
|
||||
*/
|
||||
valueType?: ValueType;
|
||||
|
||||
/**
|
||||
* The advice influencing aggregation configuration parameters.
|
||||
* @experimental
|
||||
*/
|
||||
advice?: MetricAdvice;
|
||||
}
|
||||
|
||||
/** The Type of value. It describes how the data is reported. */
|
||||
|
|
|
|||
|
|
@ -14,7 +14,12 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MetricOptions, ValueType, diag } from '@opentelemetry/api';
|
||||
import {
|
||||
MetricAdvice,
|
||||
MetricOptions,
|
||||
ValueType,
|
||||
diag,
|
||||
} from '@opentelemetry/api';
|
||||
import { View } from './view/View';
|
||||
import { equalsCaseInsensitive } from './utils';
|
||||
|
||||
|
|
@ -31,7 +36,10 @@ export enum InstrumentType {
|
|||
}
|
||||
|
||||
/**
|
||||
* An interface describing the instrument.
|
||||
* An internal interface describing the instrument.
|
||||
*
|
||||
* This is intentionally distinguished from the public MetricDescriptor (a.k.a. InstrumentDescriptor)
|
||||
* which may not contains internal fields like metric advice.
|
||||
*/
|
||||
export interface InstrumentDescriptor {
|
||||
readonly name: string;
|
||||
|
|
@ -39,6 +47,10 @@ export interface InstrumentDescriptor {
|
|||
readonly unit: string;
|
||||
readonly type: InstrumentType;
|
||||
readonly valueType: ValueType;
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
readonly advice: MetricAdvice;
|
||||
}
|
||||
|
||||
export function createInstrumentDescriptor(
|
||||
|
|
@ -57,6 +69,7 @@ export function createInstrumentDescriptor(
|
|||
description: options?.description ?? '',
|
||||
unit: options?.unit ?? '',
|
||||
valueType: options?.valueType ?? ValueType.DOUBLE,
|
||||
advice: options?.advice ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +83,7 @@ export function createInstrumentDescriptorWithView(
|
|||
type: instrument.type,
|
||||
unit: instrument.unit,
|
||||
valueType: instrument.valueType,
|
||||
advice: instrument.advice,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import {
|
|||
} from '@opentelemetry/api';
|
||||
import { AttributeHashMap } from './state/HashMap';
|
||||
import { isObservableInstrument, ObservableInstrument } from './Instruments';
|
||||
import { InstrumentDescriptor } from '.';
|
||||
|
||||
/**
|
||||
* The class implements {@link ObservableResult} interface.
|
||||
|
|
@ -35,7 +34,10 @@ export class ObservableResultImpl implements ObservableResult {
|
|||
*/
|
||||
_buffer = new AttributeHashMap<number>();
|
||||
|
||||
constructor(private _descriptor: InstrumentDescriptor) {}
|
||||
constructor(
|
||||
private _instrumentName: string,
|
||||
private _valueType: ValueType
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Observe a measurement of the value associated with the given attributes.
|
||||
|
|
@ -43,16 +45,13 @@ export class ObservableResultImpl implements ObservableResult {
|
|||
observe(value: number, attributes: MetricAttributes = {}): void {
|
||||
if (typeof value !== 'number') {
|
||||
diag.warn(
|
||||
`non-number value provided to metric ${this._descriptor.name}: ${value}`
|
||||
`non-number value provided to metric ${this._instrumentName}: ${value}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this._descriptor.valueType === ValueType.INT &&
|
||||
!Number.isInteger(value)
|
||||
) {
|
||||
if (this._valueType === ValueType.INT && !Number.isInteger(value)) {
|
||||
diag.warn(
|
||||
`INT value type cannot accept a floating-point value for ${this._descriptor.name}, ignoring the fractional digits.`
|
||||
`INT value type cannot accept a floating-point value for ${this._instrumentName}, ignoring the fractional digits.`
|
||||
);
|
||||
value = Math.trunc(value);
|
||||
// ignore non-finite values.
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
import { HrTime } from '@opentelemetry/api';
|
||||
import { AggregationTemporality } from '../export/AggregationTemporality';
|
||||
import { MetricData } from '../export/MetricData';
|
||||
import { InstrumentDescriptor } from '../InstrumentDescriptor';
|
||||
import { MetricData, MetricDescriptor } from '../export/MetricData';
|
||||
import { Maybe } from '../utils';
|
||||
import { AggregatorKind, Aggregator, AccumulationRecord } from './types';
|
||||
|
||||
|
|
@ -38,7 +37,7 @@ export class DropAggregator implements Aggregator<undefined> {
|
|||
}
|
||||
|
||||
toMetricData(
|
||||
_descriptor: InstrumentDescriptor,
|
||||
_descriptor: MetricDescriptor,
|
||||
_aggregationTemporality: AggregationTemporality,
|
||||
_accumulationByAttributes: AccumulationRecord<undefined>[],
|
||||
_endTime: HrTime
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ import {
|
|||
import {
|
||||
DataPointType,
|
||||
ExponentialHistogramMetricData,
|
||||
MetricDescriptor,
|
||||
} from '../export/MetricData';
|
||||
import { diag, HrTime } from '@opentelemetry/api';
|
||||
import { InstrumentDescriptor, InstrumentType } from '../InstrumentDescriptor';
|
||||
import { InstrumentType } from '../InstrumentDescriptor';
|
||||
import { Maybe } from '../utils';
|
||||
import { AggregationTemporality } from '../export/AggregationTemporality';
|
||||
import { Buckets } from './exponential-histogram/Buckets';
|
||||
|
|
@ -555,7 +556,7 @@ export class ExponentialHistogramAggregator
|
|||
}
|
||||
|
||||
toMetricData(
|
||||
descriptor: InstrumentDescriptor,
|
||||
descriptor: MetricDescriptor,
|
||||
aggregationTemporality: AggregationTemporality,
|
||||
accumulationByAttributes: AccumulationRecord<ExponentialHistogramAccumulation>[],
|
||||
endTime: HrTime
|
||||
|
|
|
|||
|
|
@ -20,9 +20,13 @@ import {
|
|||
Aggregator,
|
||||
AggregatorKind,
|
||||
} from './types';
|
||||
import { DataPointType, HistogramMetricData } from '../export/MetricData';
|
||||
import {
|
||||
DataPointType,
|
||||
HistogramMetricData,
|
||||
MetricDescriptor,
|
||||
} from '../export/MetricData';
|
||||
import { HrTime } from '@opentelemetry/api';
|
||||
import { InstrumentDescriptor, InstrumentType } from '../InstrumentDescriptor';
|
||||
import { InstrumentType } from '../InstrumentDescriptor';
|
||||
import { binarySearchLB, Maybe } from '../utils';
|
||||
import { AggregationTemporality } from '../export/AggregationTemporality';
|
||||
|
||||
|
|
@ -207,7 +211,7 @@ export class HistogramAggregator implements Aggregator<HistogramAccumulation> {
|
|||
}
|
||||
|
||||
toMetricData(
|
||||
descriptor: InstrumentDescriptor,
|
||||
descriptor: MetricDescriptor,
|
||||
aggregationTemporality: AggregationTemporality,
|
||||
accumulationByAttributes: AccumulationRecord<HistogramAccumulation>[],
|
||||
endTime: HrTime
|
||||
|
|
|
|||
|
|
@ -23,8 +23,11 @@ import {
|
|||
} from './types';
|
||||
import { HrTime } from '@opentelemetry/api';
|
||||
import { millisToHrTime, hrTimeToMicroseconds } from '@opentelemetry/core';
|
||||
import { DataPointType, GaugeMetricData } from '../export/MetricData';
|
||||
import { InstrumentDescriptor } from '../InstrumentDescriptor';
|
||||
import {
|
||||
DataPointType,
|
||||
GaugeMetricData,
|
||||
MetricDescriptor,
|
||||
} from '../export/MetricData';
|
||||
import { Maybe } from '../utils';
|
||||
import { AggregationTemporality } from '../export/AggregationTemporality';
|
||||
|
||||
|
|
@ -103,7 +106,7 @@ export class LastValueAggregator implements Aggregator<LastValueAccumulation> {
|
|||
}
|
||||
|
||||
toMetricData(
|
||||
descriptor: InstrumentDescriptor,
|
||||
descriptor: MetricDescriptor,
|
||||
aggregationTemporality: AggregationTemporality,
|
||||
accumulationByAttributes: AccumulationRecord<LastValueAccumulation>[],
|
||||
endTime: HrTime
|
||||
|
|
|
|||
|
|
@ -22,8 +22,11 @@ import {
|
|||
AccumulationRecord,
|
||||
} from './types';
|
||||
import { HrTime } from '@opentelemetry/api';
|
||||
import { DataPointType, SumMetricData } from '../export/MetricData';
|
||||
import { InstrumentDescriptor } from '../InstrumentDescriptor';
|
||||
import {
|
||||
DataPointType,
|
||||
MetricDescriptor,
|
||||
SumMetricData,
|
||||
} from '../export/MetricData';
|
||||
import { Maybe } from '../utils';
|
||||
import { AggregationTemporality } from '../export/AggregationTemporality';
|
||||
|
||||
|
|
@ -109,7 +112,7 @@ export class SumAggregator implements Aggregator<SumAccumulation> {
|
|||
}
|
||||
|
||||
toMetricData(
|
||||
descriptor: InstrumentDescriptor,
|
||||
descriptor: MetricDescriptor,
|
||||
aggregationTemporality: AggregationTemporality,
|
||||
accumulationByAttributes: AccumulationRecord<SumAccumulation>[],
|
||||
endTime: HrTime
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@
|
|||
|
||||
import { HrTime, MetricAttributes } from '@opentelemetry/api';
|
||||
import { AggregationTemporality } from '../export/AggregationTemporality';
|
||||
import { MetricData } from '../export/MetricData';
|
||||
import { InstrumentDescriptor } from '../InstrumentDescriptor';
|
||||
import { MetricData, MetricDescriptor } from '../export/MetricData';
|
||||
import { Maybe } from '../utils';
|
||||
|
||||
/** The kind of aggregator. */
|
||||
|
|
@ -128,14 +127,14 @@ export interface Aggregator<T> {
|
|||
/**
|
||||
* Returns the {@link MetricData} that this {@link Aggregator} will produce.
|
||||
*
|
||||
* @param descriptor the metric instrument descriptor.
|
||||
* @param descriptor the metric descriptor.
|
||||
* @param aggregationTemporality the temporality of the resulting {@link MetricData}
|
||||
* @param accumulationByAttributes the array of attributes and accumulation pairs.
|
||||
* @param endTime the end time of the metric data.
|
||||
* @return the {@link MetricData} that this {@link Aggregator} will produce.
|
||||
*/
|
||||
toMetricData(
|
||||
descriptor: InstrumentDescriptor,
|
||||
descriptor: MetricDescriptor,
|
||||
aggregationTemporality: AggregationTemporality,
|
||||
accumulationByAttributes: AccumulationRecord<T>[],
|
||||
endTime: HrTime
|
||||
|
|
|
|||
|
|
@ -14,18 +14,30 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { HrTime, MetricAttributes } from '@opentelemetry/api';
|
||||
import { HrTime, MetricAttributes, ValueType } from '@opentelemetry/api';
|
||||
import { InstrumentationScope } from '@opentelemetry/core';
|
||||
import { IResource } from '@opentelemetry/resources';
|
||||
import { InstrumentDescriptor } from '../InstrumentDescriptor';
|
||||
import { InstrumentType } from '../InstrumentDescriptor';
|
||||
import { AggregationTemporality } from './AggregationTemporality';
|
||||
import { Histogram, ExponentialHistogram } from '../aggregator/types';
|
||||
|
||||
export interface MetricDescriptor {
|
||||
readonly name: string;
|
||||
readonly description: string;
|
||||
readonly unit: string;
|
||||
/**
|
||||
* @deprecated exporter should avoid depending on the type of the instrument
|
||||
* as their resulting aggregator can be re-mapped with views.
|
||||
*/
|
||||
readonly type: InstrumentType;
|
||||
readonly valueType: ValueType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic metric data fields.
|
||||
*/
|
||||
interface BaseMetricData {
|
||||
readonly descriptor: InstrumentDescriptor;
|
||||
readonly descriptor: MetricDescriptor;
|
||||
readonly aggregationTemporality: AggregationTemporality;
|
||||
/**
|
||||
* DataPointType of the metric instrument.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MetricDescriptor } from './export/MetricData';
|
||||
|
||||
export {
|
||||
Sum,
|
||||
LastValue,
|
||||
|
|
@ -38,6 +40,7 @@ export {
|
|||
ResourceMetrics,
|
||||
ScopeMetrics,
|
||||
MetricData,
|
||||
MetricDescriptor,
|
||||
CollectionResult,
|
||||
} from './export/MetricData';
|
||||
|
||||
|
|
@ -56,7 +59,11 @@ export { ConsoleMetricExporter } from './export/ConsoleMetricExporter';
|
|||
|
||||
export { MetricCollectOptions, MetricProducer } from './export/MetricProducer';
|
||||
|
||||
export { InstrumentDescriptor, InstrumentType } from './InstrumentDescriptor';
|
||||
export { InstrumentType } from './InstrumentDescriptor';
|
||||
/**
|
||||
* @deprecated Use {@link MetricDescriptor} instead.
|
||||
*/
|
||||
export type InstrumentDescriptor = MetricDescriptor;
|
||||
|
||||
export { MeterProvider, MeterProviderOptions } from './MeterProvider';
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ export abstract class MetricStorage {
|
|||
description: description,
|
||||
valueType: this._instrumentDescriptor.valueType,
|
||||
unit: this._instrumentDescriptor.unit,
|
||||
advice: this._instrumentDescriptor.advice,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -144,7 +144,10 @@ export class ObservableRegistry {
|
|||
|
||||
private _observeCallbacks(observationTime: HrTime, timeoutMillis?: number) {
|
||||
return this._callbacks.map(async ({ callback, instrument }) => {
|
||||
const observableResult = new ObservableResultImpl(instrument._descriptor);
|
||||
const observableResult = new ObservableResultImpl(
|
||||
instrument._descriptor.name,
|
||||
instrument._descriptor.valueType
|
||||
);
|
||||
let callPromise: Promise<void> = Promise.resolve(
|
||||
callback(observableResult)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -184,6 +184,11 @@ export class DefaultAggregation extends Aggregation {
|
|||
return LAST_VALUE_AGGREGATION;
|
||||
}
|
||||
case InstrumentType.HISTOGRAM: {
|
||||
if (instrument.advice.explicitBucketBoundaries) {
|
||||
return new ExplicitBucketHistogramAggregation(
|
||||
instrument.advice.explicitBucketBoundaries
|
||||
);
|
||||
}
|
||||
return HISTOGRAM_AGGREGATION;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: 'kB',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
{
|
||||
name: 'foo',
|
||||
|
|
@ -64,6 +65,7 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: 'kB',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
],
|
||||
[
|
||||
|
|
@ -75,6 +77,7 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
{
|
||||
name: 'FoO',
|
||||
|
|
@ -82,6 +85,53 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
],
|
||||
[
|
||||
'Compatible with different advice options',
|
||||
true,
|
||||
{
|
||||
name: 'foo',
|
||||
description: '',
|
||||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {
|
||||
explicitBucketBoundaries: [4, 5, 6],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'FoO',
|
||||
description: '',
|
||||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {
|
||||
explicitBucketBoundaries: [1, 2, 3],
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
'Compatible with empty advice options',
|
||||
true,
|
||||
{
|
||||
name: 'foo',
|
||||
description: '',
|
||||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
{
|
||||
name: 'FoO',
|
||||
description: '',
|
||||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {
|
||||
explicitBucketBoundaries: [1, 2, 3],
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
|
|
@ -93,6 +143,7 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
{
|
||||
name: 'foobar',
|
||||
|
|
@ -100,6 +151,7 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
],
|
||||
[
|
||||
|
|
@ -111,6 +163,7 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: 'kB',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
{
|
||||
name: 'foo',
|
||||
|
|
@ -118,6 +171,7 @@ describe('InstrumentDescriptor', () => {
|
|||
unit: 'kb',
|
||||
type: InstrumentType.COUNTER,
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
},
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ import * as sinon from 'sinon';
|
|||
import { InstrumentationScope } from '@opentelemetry/core';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
import {
|
||||
InstrumentDescriptor,
|
||||
InstrumentType,
|
||||
MeterProvider,
|
||||
MetricReader,
|
||||
DataPoint,
|
||||
DataPointType,
|
||||
Histogram,
|
||||
MetricDescriptor,
|
||||
} from '../src';
|
||||
import {
|
||||
TestDeltaMetricReader,
|
||||
|
|
@ -352,6 +352,60 @@ describe('Instruments', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should recognize metric advice', async () => {
|
||||
const { meter, deltaReader } = setup();
|
||||
const histogram = meter.createHistogram('test', {
|
||||
valueType: ValueType.INT,
|
||||
advice: {
|
||||
// Set explicit boundaries that are different from the default one.
|
||||
explicitBucketBoundaries: [1, 9, 100],
|
||||
},
|
||||
});
|
||||
|
||||
histogram.record(10);
|
||||
histogram.record(0);
|
||||
histogram.record(100, { foo: 'bar' });
|
||||
histogram.record(0, { foo: 'bar' });
|
||||
await validateExport(deltaReader, {
|
||||
descriptor: {
|
||||
name: 'test',
|
||||
description: '',
|
||||
unit: '',
|
||||
type: InstrumentType.HISTOGRAM,
|
||||
valueType: ValueType.INT,
|
||||
},
|
||||
dataPointType: DataPointType.HISTOGRAM,
|
||||
dataPoints: [
|
||||
{
|
||||
attributes: {},
|
||||
value: {
|
||||
buckets: {
|
||||
boundaries: [1, 9, 100],
|
||||
counts: [1, 0, 1, 0],
|
||||
},
|
||||
count: 2,
|
||||
sum: 10,
|
||||
max: 10,
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
attributes: { foo: 'bar' },
|
||||
value: {
|
||||
buckets: {
|
||||
boundaries: [1, 9, 100],
|
||||
counts: [1, 0, 0, 1],
|
||||
},
|
||||
count: 2,
|
||||
sum: 100,
|
||||
max: 100,
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should collect min and max', async () => {
|
||||
const { meter, deltaReader, cumulativeReader } = setup();
|
||||
const histogram = meter.createHistogram('test', {
|
||||
|
|
@ -721,7 +775,7 @@ function setup() {
|
|||
interface ValidateMetricData {
|
||||
resource?: Resource;
|
||||
instrumentationScope?: InstrumentationScope;
|
||||
descriptor?: InstrumentDescriptor;
|
||||
descriptor?: MetricDescriptor;
|
||||
dataPointType?: DataPointType;
|
||||
dataPoints?: Partial<DataPoint<number | Partial<Histogram>>>[];
|
||||
isMonotonic?: boolean;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,8 @@ describe('ObservableResultImpl', () => {
|
|||
describe('observe', () => {
|
||||
it('should observe common values', () => {
|
||||
const observableResult = new ObservableResultImpl(
|
||||
defaultInstrumentDescriptor
|
||||
'instrument_name',
|
||||
ValueType.DOUBLE
|
||||
);
|
||||
for (const value of commonValues) {
|
||||
for (const attributes of commonAttributes) {
|
||||
|
|
@ -44,7 +45,8 @@ describe('ObservableResultImpl', () => {
|
|||
|
||||
it('should deduplicate observations', () => {
|
||||
const observableResult = new ObservableResultImpl(
|
||||
defaultInstrumentDescriptor
|
||||
'instrument_name',
|
||||
ValueType.DOUBLE
|
||||
);
|
||||
observableResult.observe(1, {});
|
||||
observableResult.observe(2, {});
|
||||
|
|
@ -55,13 +57,10 @@ describe('ObservableResultImpl', () => {
|
|||
});
|
||||
|
||||
it('should trunc value if ValueType is INT', () => {
|
||||
const observableResult = new ObservableResultImpl({
|
||||
name: 'test',
|
||||
description: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
unit: '',
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
const observableResult = new ObservableResultImpl(
|
||||
'instrument_name',
|
||||
ValueType.INT
|
||||
);
|
||||
observableResult.observe(1.1, {});
|
||||
// should ignore non-finite/non-number values.
|
||||
observableResult.observe(Infinity, {});
|
||||
|
|
@ -72,14 +71,7 @@ describe('ObservableResultImpl', () => {
|
|||
});
|
||||
|
||||
it('should ignore non-number values', () => {
|
||||
const observableResult = new ObservableResultImpl({
|
||||
name: 'test',
|
||||
description: '',
|
||||
type: InstrumentType.COUNTER,
|
||||
unit: '',
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
|
||||
const observableResult = new ObservableResultImpl('test', ValueType.INT);
|
||||
observableResult.observe('1' as any, {});
|
||||
|
||||
assert.strictEqual(observableResult._buffer.get({}), undefined);
|
||||
|
|
@ -139,6 +131,7 @@ describe('BatchObservableResultImpl', () => {
|
|||
type: InstrumentType.COUNTER,
|
||||
unit: '',
|
||||
valueType: ValueType.INT,
|
||||
advice: {},
|
||||
},
|
||||
[],
|
||||
new ObservableRegistry()
|
||||
|
|
@ -161,6 +154,7 @@ describe('BatchObservableResultImpl', () => {
|
|||
type: InstrumentType.COUNTER,
|
||||
unit: '',
|
||||
valueType: ValueType.INT,
|
||||
advice: {},
|
||||
},
|
||||
[],
|
||||
new ObservableRegistry()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import { diag, ValueType } from '@opentelemetry/api';
|
|||
import { MetricStorage } from '../../src/state/MetricStorage';
|
||||
import { HrTime } from '@opentelemetry/api';
|
||||
import { MetricCollectorHandle } from '../../src/state/MetricCollector';
|
||||
import { MetricData, InstrumentDescriptor, InstrumentType } from '../../src';
|
||||
import { MetricData, InstrumentType } from '../../src';
|
||||
import { Maybe } from '../../src/utils';
|
||||
import * as assert from 'assert';
|
||||
import * as sinon from 'sinon';
|
||||
|
|
@ -29,6 +29,7 @@ import {
|
|||
getUnitConflictResolutionRecipe,
|
||||
getValueTypeConflictResolutionRecipe,
|
||||
} from '../../src/view/RegistrationConflicts';
|
||||
import { InstrumentDescriptor } from '../../src/InstrumentDescriptor';
|
||||
|
||||
class TestMetricStorage extends MetricStorage {
|
||||
collect(
|
||||
|
|
@ -73,6 +74,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
});
|
||||
|
||||
registry.register(storage);
|
||||
|
|
@ -92,6 +94,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
});
|
||||
const storage2 = new TestMetricStorage({
|
||||
name: 'instrument2',
|
||||
|
|
@ -99,6 +102,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
});
|
||||
|
||||
registry.registerForCollector(collectorHandle, storage);
|
||||
|
|
@ -152,6 +156,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const otherDescriptor = {
|
||||
|
|
@ -160,6 +165,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
testConflictingRegistration(
|
||||
|
|
@ -176,6 +182,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const otherDescriptor = {
|
||||
|
|
@ -184,6 +191,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.INT,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
testConflictingRegistration(
|
||||
|
|
@ -203,6 +211,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const otherDescriptor = {
|
||||
|
|
@ -211,6 +220,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: 'ms',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
testConflictingRegistration(
|
||||
|
|
@ -227,6 +237,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const otherDescriptor = {
|
||||
|
|
@ -235,6 +246,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'longer description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const registry = new MetricStorageRegistry();
|
||||
|
|
@ -275,6 +287,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const storage = new TestMetricStorage(descriptor);
|
||||
|
|
@ -294,6 +307,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const storage = new TestMetricStorage(descriptor);
|
||||
|
|
@ -329,6 +343,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const otherDescriptor = {
|
||||
|
|
@ -337,6 +352,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
const registry = new MetricStorageRegistry();
|
||||
|
|
@ -375,6 +391,7 @@ describe('MetricStorageRegistry', () => {
|
|||
description: 'description',
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
const registry = new MetricStorageRegistry();
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import {
|
|||
DataPoint,
|
||||
DataPointType,
|
||||
ScopeMetrics,
|
||||
MetricDescriptor,
|
||||
} from '../src/export/MetricData';
|
||||
import { isNotNullish } from '../src/utils';
|
||||
import { HrTime } from '@opentelemetry/api';
|
||||
|
|
@ -58,6 +59,7 @@ export const defaultInstrumentDescriptor: InstrumentDescriptor = {
|
|||
type: InstrumentType.COUNTER,
|
||||
unit: '1',
|
||||
valueType: ValueType.DOUBLE,
|
||||
advice: {},
|
||||
};
|
||||
|
||||
export const defaultInstrumentationScope: InstrumentationScope = {
|
||||
|
|
@ -104,12 +106,12 @@ export function assertScopeMetrics(
|
|||
export function assertMetricData(
|
||||
actual: unknown,
|
||||
dataPointType?: DataPointType,
|
||||
instrumentDescriptor: Partial<InstrumentDescriptor> | null = defaultInstrumentDescriptor,
|
||||
metricDescriptor: Partial<MetricDescriptor> | null = defaultInstrumentDescriptor,
|
||||
aggregationTemporality?: AggregationTemporality
|
||||
): asserts actual is MetricData {
|
||||
const it = actual as MetricData;
|
||||
if (instrumentDescriptor != null) {
|
||||
assertPartialDeepStrictEqual(it.descriptor, instrumentDescriptor);
|
||||
if (metricDescriptor != null) {
|
||||
assertPartialDeepStrictEqual(it.descriptor, metricDescriptor);
|
||||
}
|
||||
if (isNotNullish(dataPointType)) {
|
||||
assert.strictEqual(it.dataPointType, dataPointType);
|
||||
|
|
|
|||
Loading…
Reference in New Issue