Skip to content

Commit 4cffe5d

Browse files
fix(sdk-metrics): ignore invalid metric values (#3988)
Co-authored-by: Haddas Bronfman <[email protected]>
1 parent 87fff2e commit 4cffe5d

File tree

5 files changed

+107
-4
lines changed

5 files changed

+107
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ For experimental package changes, see the [experimental CHANGELOG](experimental/
1515

1616
* fix(opentelemetry-exporter-logs-otlp-http): Add otel-api as dev dep for tests as they are directly importing the api and which is breaking the web-sandbox tests which is using rollup
1717
* fix(core): stop rounding to nearest int in hrTimeTo*seconds() functions [#4014](https://github.com/open-telemetry/opentelemetry-js/pull/4014/) @aabmass
18+
* fix(sdk-metrics): ignore invalid metric values [#3988](https://github.com/open-telemetry/opentelemetry-js/pull/3988) @legendecas
1819

1920
### :books: (Refine Doc)
2021

packages/sdk-metrics/src/Instruments.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ export class SyncInstrument {
4848
attributes: MetricAttributes = {},
4949
context: Context = contextApi.active()
5050
) {
51+
if (typeof value !== 'number') {
52+
diag.warn(
53+
`non-number value provided to metric ${this._descriptor.name}: ${value}`
54+
);
55+
return;
56+
}
5157
if (
5258
this._descriptor.valueType === ValueType.INT &&
5359
!Number.isInteger(value)
@@ -56,6 +62,10 @@ export class SyncInstrument {
5662
`INT value type cannot accept a floating-point value for ${this._descriptor.name}, ignoring the fractional digits.`
5763
);
5864
value = Math.trunc(value);
65+
// ignore non-finite values.
66+
if (!Number.isInteger(value)) {
67+
return;
68+
}
5969
}
6070
this._writableMetricStorage.record(
6171
value,

packages/sdk-metrics/src/ObservableResult.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ export class ObservableResultImpl implements ObservableResult {
4141
* Observe a measurement of the value associated with the given attributes.
4242
*/
4343
observe(value: number, attributes: MetricAttributes = {}): void {
44+
if (typeof value !== 'number') {
45+
diag.warn(
46+
`non-number value provided to metric ${this._descriptor.name}: ${value}`
47+
);
48+
return;
49+
}
4450
if (
4551
this._descriptor.valueType === ValueType.INT &&
4652
!Number.isInteger(value)
@@ -49,6 +55,10 @@ export class ObservableResultImpl implements ObservableResult {
4955
`INT value type cannot accept a floating-point value for ${this._descriptor.name}, ignoring the fractional digits.`
5056
);
5157
value = Math.trunc(value);
58+
// ignore non-finite values.
59+
if (!Number.isInteger(value)) {
60+
return;
61+
}
5262
}
5363
this._buffer.set(attributes, value);
5464
}
@@ -79,6 +89,12 @@ export class BatchObservableResultImpl implements BatchObservableResult {
7989
map = new AttributeHashMap();
8090
this._buffer.set(metric, map);
8191
}
92+
if (typeof value !== 'number') {
93+
diag.warn(
94+
`non-number value provided to metric ${metric._descriptor.name}: ${value}`
95+
);
96+
return;
97+
}
8298
if (
8399
metric._descriptor.valueType === ValueType.INT &&
84100
!Number.isInteger(value)
@@ -87,6 +103,10 @@ export class BatchObservableResultImpl implements BatchObservableResult {
87103
`INT value type cannot accept a floating-point value for ${metric._descriptor.name}, ignoring the fractional digits.`
88104
);
89105
value = Math.trunc(value);
106+
// ignore non-finite values.
107+
if (!Number.isInteger(value)) {
108+
return;
109+
}
90110
}
91111
map.set(attributes, value);
92112
}

packages/sdk-metrics/test/Instruments.test.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,14 @@ describe('Instruments', () => {
7474
});
7575

7676
counter.add(1);
77-
// floating-point value should be trunc-ed.
78-
counter.add(1.1);
7977
counter.add(1, { foo: 'bar' });
78+
// floating-point values should be trunc-ed.
79+
counter.add(1.1);
80+
// non-finite/non-number values should be ignored.
81+
counter.add(Infinity);
82+
counter.add(-Infinity);
83+
counter.add(NaN);
84+
counter.add('1' as any);
8085
await validateExport(cumulativeReader, {
8186
descriptor: {
8287
name: 'test',
@@ -124,10 +129,13 @@ describe('Instruments', () => {
124129
});
125130

126131
counter.add(1);
127-
// add floating-point value.
128-
counter.add(1.1);
129132
counter.add(1, { foo: 'bar' });
133+
// add floating-point values.
134+
counter.add(1.1);
130135
counter.add(1.2, { foo: 'bar' });
136+
// non-number values should be ignored.
137+
counter.add('1' as any);
138+
131139
await validateExport(cumulativeReader, {
132140
dataPointType: DataPointType.SUM,
133141
isMonotonic: true,
@@ -197,6 +205,13 @@ describe('Instruments', () => {
197205
upDownCounter.add(-1.1);
198206
upDownCounter.add(4, { foo: 'bar' });
199207
upDownCounter.add(1.1, { foo: 'bar' });
208+
209+
// non-finite/non-number values should be ignored.
210+
upDownCounter.add(Infinity);
211+
upDownCounter.add(-Infinity);
212+
upDownCounter.add(NaN);
213+
upDownCounter.add('1' as any);
214+
200215
await validateExport(deltaReader, {
201216
descriptor: {
202217
name: 'test',
@@ -230,6 +245,8 @@ describe('Instruments', () => {
230245
upDownCounter.add(-1.1);
231246
upDownCounter.add(4, { foo: 'bar' });
232247
upDownCounter.add(1.1, { foo: 'bar' });
248+
// non-number values should be ignored.
249+
upDownCounter.add('1' as any);
233250
await validateExport(deltaReader, {
234251
dataPointType: DataPointType.SUM,
235252
isMonotonic: false,
@@ -283,6 +300,12 @@ describe('Instruments', () => {
283300
histogram.record(0.1);
284301
histogram.record(100, { foo: 'bar' });
285302
histogram.record(0.1, { foo: 'bar' });
303+
// non-finite/non-number values should be ignored.
304+
histogram.record(Infinity);
305+
histogram.record(-Infinity);
306+
histogram.record(NaN);
307+
histogram.record('1' as any);
308+
286309
await validateExport(deltaReader, {
287310
descriptor: {
288311
name: 'test',
@@ -427,6 +450,9 @@ describe('Instruments', () => {
427450
histogram.record(0.1);
428451
histogram.record(100, { foo: 'bar' });
429452
histogram.record(0.1, { foo: 'bar' });
453+
// non-number values should be ignored.
454+
histogram.record('1' as any);
455+
430456
await validateExport(deltaReader, {
431457
dataPointType: DataPointType.HISTOGRAM,
432458
dataPoints: [

packages/sdk-metrics/test/ObservableResult.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,27 @@ describe('ObservableResultImpl', () => {
6363
valueType: ValueType.INT,
6464
});
6565
observableResult.observe(1.1, {});
66+
// should ignore non-finite/non-number values.
67+
observableResult.observe(Infinity, {});
68+
observableResult.observe(-Infinity, {});
69+
observableResult.observe(NaN, {});
70+
6671
assert.strictEqual(observableResult._buffer.get({}), 1);
6772
});
73+
74+
it('should ignore non-number values', () => {
75+
const observableResult = new ObservableResultImpl({
76+
name: 'test',
77+
description: '',
78+
type: InstrumentType.COUNTER,
79+
unit: '',
80+
valueType: ValueType.INT,
81+
});
82+
83+
observableResult.observe('1' as any, {});
84+
85+
assert.strictEqual(observableResult._buffer.get({}), undefined);
86+
});
6887
});
6988
});
7089

@@ -126,7 +145,34 @@ describe('BatchObservableResultImpl', () => {
126145
);
127146

128147
observableResult.observe(observable, 1.1, {});
148+
// should ignore non-finite/non-number values.
149+
observableResult.observe(observable, Infinity, {});
150+
observableResult.observe(observable, -Infinity, {});
151+
observableResult.observe(observable, NaN, {});
129152
assert.strictEqual(observableResult._buffer.get(observable)?.get({}), 1);
130153
});
154+
155+
it('should ignore invalid values', () => {
156+
const observableResult = new BatchObservableResultImpl();
157+
const observable = new ObservableInstrument(
158+
{
159+
name: 'test',
160+
description: '',
161+
type: InstrumentType.COUNTER,
162+
unit: '',
163+
valueType: ValueType.INT,
164+
},
165+
[],
166+
new ObservableRegistry()
167+
);
168+
169+
observableResult.observe(observable, '1' as any, {});
170+
observableResult.observe(/** invalid observable */ {} as any, 1, {});
171+
assert.strictEqual(
172+
observableResult._buffer.get(observable)!.get({}),
173+
undefined
174+
);
175+
assert.strictEqual(observableResult._buffer.size, 1);
176+
});
131177
});
132178
});

0 commit comments

Comments
 (0)