Skip to content

Commit a576c3f

Browse files
legendecasobecnydyladan
authored
fix: ignore non-number value on BaseBoundInstrument.update (#1366)
Co-authored-by: Bartlomiej Obecny <[email protected]> Co-authored-by: Daniel Dyla <[email protected]>
1 parent a3357d5 commit a576c3f

File tree

2 files changed

+114
-0
lines changed

2 files changed

+114
-0
lines changed

packages/opentelemetry-metrics/src/BoundInstrument.ts

+8
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ export class BaseBoundInstrument {
3838

3939
update(value: number): void {
4040
if (this._disabled) return;
41+
if (typeof value !== 'number') {
42+
this._logger.error(
43+
`Metric cannot accept a non-number value for ${Object.values(
44+
this._labels
45+
)}.`
46+
);
47+
return;
48+
}
4149

4250
if (this._valueType === api.ValueType.INT && !Number.isInteger(value)) {
4351
this._logger.warn(

packages/opentelemetry-metrics/test/Meter.test.ts

+106
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,35 @@ import { Resource } from '@opentelemetry/resources';
4141
import { UpDownSumObserverMetric } from '../src/UpDownSumObserverMetric';
4242
import { hashLabels } from '../src/Utils';
4343
import { Batcher } from '../src/export/Batcher';
44+
import { ValueType } from '@opentelemetry/api';
45+
46+
const nonNumberValues = [
47+
// type undefined
48+
undefined,
49+
// type null
50+
null,
51+
// type function
52+
function () {},
53+
// type boolean
54+
true,
55+
false,
56+
// type string
57+
'1',
58+
// type object
59+
{},
60+
// type symbol
61+
// symbols cannot be cast to number, early errors will be thrown.
62+
];
63+
64+
if (Number(process.versions.node.match(/^\d+/)) >= 10) {
65+
nonNumberValues.push(
66+
// type bigint
67+
// Preferring BigInt builtin object instead of bigint literal to keep Node.js v8.x working.
68+
// TODO: should metric instruments support bigint?
69+
// @ts-ignore
70+
BigInt(1) // eslint-disable-line node/no-unsupported-features/es-builtins
71+
);
72+
}
4473

4574
describe('Meter', () => {
4675
let meter: Meter;
@@ -374,6 +403,56 @@ describe('Meter', () => {
374403
assert.strictEqual(record1.aggregator.toPoint().value, 20);
375404
assert.strictEqual(boundCounter, boundCounter1);
376405
});
406+
407+
it('should truncate non-integer values for INT valueType', async () => {
408+
const upDownCounter = meter.createUpDownCounter('name', {
409+
valueType: ValueType.INT,
410+
});
411+
const boundCounter = upDownCounter.bind(labels);
412+
413+
[-1.1, 2.2].forEach(val => {
414+
boundCounter.add(val);
415+
});
416+
await meter.collect();
417+
const [record1] = meter.getBatcher().checkPointSet();
418+
assert.strictEqual(record1.aggregator.toPoint().value, 1);
419+
});
420+
421+
it('should ignore non-number values for INT valueType', async () => {
422+
const upDownCounter = meter.createUpDownCounter('name', {
423+
valueType: ValueType.DOUBLE,
424+
});
425+
const boundCounter = upDownCounter.bind(labels);
426+
427+
await Promise.all(
428+
nonNumberValues.map(async val => {
429+
// @ts-expect-error
430+
boundCounter.add(val);
431+
await meter.collect();
432+
const [record1] = meter.getBatcher().checkPointSet();
433+
434+
assert.strictEqual(record1.aggregator.toPoint().value, 0);
435+
})
436+
);
437+
});
438+
439+
it('should ignore non-number values for DOUBLE valueType', async () => {
440+
const upDownCounter = meter.createUpDownCounter('name', {
441+
valueType: ValueType.DOUBLE,
442+
});
443+
const boundCounter = upDownCounter.bind(labels);
444+
445+
await Promise.all(
446+
nonNumberValues.map(async val => {
447+
// @ts-expect-error
448+
boundCounter.add(val);
449+
await meter.collect();
450+
const [record1] = meter.getBatcher().checkPointSet();
451+
452+
assert.strictEqual(record1.aggregator.toPoint().value, 0);
453+
})
454+
);
455+
});
377456
});
378457

379458
describe('.unbind()', () => {
@@ -651,6 +730,33 @@ describe('Meter', () => {
651730
);
652731
assert.strictEqual(boundValueRecorder1, boundValueRecorder2);
653732
});
733+
734+
it('should ignore non-number values', async () => {
735+
const valueRecorder = meter.createValueRecorder(
736+
'name'
737+
) as ValueRecorderMetric;
738+
const boundValueRecorder = valueRecorder.bind(labels);
739+
740+
await Promise.all(
741+
nonNumberValues.map(async val => {
742+
// @ts-expect-error
743+
boundValueRecorder.record(val);
744+
await meter.collect();
745+
const [record1] = meter.getBatcher().checkPointSet();
746+
747+
assert.deepStrictEqual(
748+
record1.aggregator.toPoint().value as Distribution,
749+
{
750+
count: 0,
751+
last: 0,
752+
max: -Infinity,
753+
min: Infinity,
754+
sum: 0,
755+
}
756+
);
757+
})
758+
);
759+
});
654760
});
655761

656762
describe('.unbind()', () => {

0 commit comments

Comments
 (0)