Skip to content

Commit 6bdc5fd

Browse files
authored
Validate add/record operations for instruments (#2394)
1 parent fb60538 commit 6bdc5fd

File tree

2 files changed

+61
-3
lines changed

2 files changed

+61
-3
lines changed

opentelemetry-sdk/src/opentelemetry/sdk/_metrics/instrument.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
# pylint: disable=too-many-ancestors
1616

17+
import logging
1718
from typing import Dict, Generator, Iterable, Union
1819

1920
from opentelemetry._metrics.instrument import CallbackT
@@ -33,6 +34,8 @@
3334
from opentelemetry.sdk._metrics.measurement_consumer import MeasurementConsumer
3435
from opentelemetry.sdk.util.instrumentation import InstrumentationInfo
3536

37+
_logger = logging.getLogger(__name__)
38+
3639

3740
class _Synchronous:
3841
def __init__(
@@ -87,8 +90,10 @@ def add(
8790
self, amount: Union[int, float], attributes: Dict[str, str] = None
8891
):
8992
if amount < 0:
90-
raise Exception("amount must be non negative")
91-
93+
_logger.warning(
94+
"Add amount must be non-negative on Counter %s.", self.name
95+
)
96+
return
9297
self._measurement_consumer.consume_measurement(
9398
Measurement(amount, attributes)
9499
)
@@ -112,7 +117,15 @@ class ObservableUpDownCounter(_Asynchronous, APIObservableUpDownCounter):
112117

113118

114119
class Histogram(_Synchronous, APIHistogram):
115-
def record(self, amount, attributes=None):
120+
def record(
121+
self, amount: Union[int, float], attributes: Dict[str, str] = None
122+
):
123+
if amount < 0:
124+
_logger.warning(
125+
"Record amount must be non-negative on Histogram %s.",
126+
self.name,
127+
)
128+
return
116129
self._measurement_consumer.consume_measurement(
117130
Measurement(amount, attributes)
118131
)

opentelemetry-sdk/tests/metrics/test_instrument.py

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,43 @@
1616
from unittest.mock import Mock
1717

1818
from opentelemetry.sdk._metrics.instrument import (
19+
Counter,
20+
Histogram,
1921
ObservableCounter,
2022
ObservableGauge,
2123
ObservableUpDownCounter,
24+
UpDownCounter,
2225
)
2326

2427

28+
class TestCounter(TestCase):
29+
def test_add(self):
30+
mc = Mock()
31+
counter = Counter("name", Mock(), mc)
32+
counter.add(1.0)
33+
mc.consume_measurement.assert_called_once()
34+
35+
def test_add_non_monotonic(self):
36+
mc = Mock()
37+
counter = Counter("name", Mock(), mc)
38+
counter.add(-1.0)
39+
mc.consume_measurement.assert_not_called()
40+
41+
42+
class TestUpDownCounter(TestCase):
43+
def test_add(self):
44+
mc = Mock()
45+
counter = UpDownCounter("name", Mock(), mc)
46+
counter.add(1.0)
47+
mc.consume_measurement.assert_called_once()
48+
49+
def test_add_non_monotonic(self):
50+
mc = Mock()
51+
counter = UpDownCounter("name", Mock(), mc)
52+
counter.add(-1.0)
53+
mc.consume_measurement.assert_called_once()
54+
55+
2556
class TestObservableGauge(TestCase):
2657
def test_callable_callback(self):
2758
def callback():
@@ -82,3 +113,17 @@ def callback():
82113
)
83114

84115
self.assertEqual(observable_up_down_counter.callback(), [1, 2, 3])
116+
117+
118+
class TestHistogram(TestCase):
119+
def test_record(self):
120+
mc = Mock()
121+
hist = Histogram("name", Mock(), mc)
122+
hist.record(1.0)
123+
mc.consume_measurement.assert_called_once()
124+
125+
def test_record_non_monotonic(self):
126+
mc = Mock()
127+
hist = Histogram("name", Mock(), mc)
128+
hist.record(-1.0)
129+
mc.consume_measurement.assert_not_called()

0 commit comments

Comments
 (0)