Skip to content

Commit 0e77604

Browse files
committed
warn if Counter used incorrectly
1 parent 2ae7d57 commit 0e77604

4 files changed

Lines changed: 65 additions & 7 deletions

File tree

opentelemetry-api/src/opentelemetry/metrics/__init__.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,17 @@ def add(self, value: ValueT) -> None:
6666
"""Increases the value of the bound counter by ``value``.
6767
6868
Args:
69-
value: The value to add to the bound counter.
69+
value: The value to add to the bound counter. Must be positive.
70+
"""
71+
72+
73+
class BoundUpDownCounter:
74+
def add(self, value: ValueT) -> None:
75+
"""Increases the value of the bound counter by ``value``.
76+
77+
Args:
78+
value: The value to add to the bound counter. Can be positive or
79+
negative.
7080
"""
7181

7282

@@ -148,9 +158,9 @@ class UpDownCounter(Metric):
148158
"""A counter type metric that expresses the computation of a sum,
149159
allowing negative increments."""
150160

151-
def bind(self, labels: Dict[str, str]) -> "BoundCounter":
152-
"""Gets a `BoundCounter`."""
153-
return BoundCounter()
161+
def bind(self, labels: Dict[str, str]) -> "BoundUpDownCounter":
162+
"""Gets a `BoundUpDownCounter`."""
163+
return BoundUpDownCounter()
154164

155165
def add(self, value: ValueT, labels: Dict[str, str]) -> None:
156166
"""Increases the value of the counter by ``value``.

opentelemetry-api/tests/metrics/test_metrics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def test_counter_add(self):
3838
def test_updowncounter(self):
3939
counter = metrics.UpDownCounter()
4040
bound_counter = counter.bind({})
41-
self.assertIsInstance(bound_counter, metrics.BoundCounter)
41+
self.assertIsInstance(bound_counter, metrics.BoundUpDownCounter)
4242

4343
def test_updowncounter_add(self):
4444
counter = metrics.Counter()

opentelemetry-sdk/src/opentelemetry/sdk/metrics/__init__.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,25 @@ def add(self, value: metrics_api.ValueT) -> None:
102102
if self._validate_update(value):
103103
self.update(value)
104104

105+
def _validate_update(self, value: metrics_api.ValueT) -> bool:
106+
if super()._validate_update(value):
107+
if value >= 0:
108+
return True
109+
logger.warning(
110+
"Invalid value %s passed to Counter, value must be "
111+
"non-negative. For a Counter that can decrease, use "
112+
"UpDownCounter.",
113+
value,
114+
)
115+
return False
116+
117+
118+
class BoundUpDownCounter(metrics_api.BoundUpDownCounter, BaseBoundInstrument):
119+
def add(self, value: metrics_api.ValueT) -> None:
120+
"""See `opentelemetry.metrics.BoundUpDownCounter.add`."""
121+
if self._validate_update(value):
122+
self.update(value)
123+
105124

106125
class BoundValueRecorder(metrics_api.BoundValueRecorder, BaseBoundInstrument):
107126
def record(self, value: metrics_api.ValueT) -> None:
@@ -188,7 +207,7 @@ class UpDownCounter(Metric, metrics_api.UpDownCounter):
188207
"""See `opentelemetry.metrics.UpDownCounter`.
189208
"""
190209

191-
BOUND_INSTR_TYPE = BoundCounter
210+
BOUND_INSTR_TYPE = BoundUpDownCounter
192211

193212
def add(self, value: metrics_api.ValueT, labels: Dict[str, str]) -> None:
194213
"""See `opentelemetry.metrics.UpDownCounter.add`."""

opentelemetry-sdk/tests/metrics/test_metrics.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,37 @@ def test_add(self):
305305
metric.add(2, labels)
306306
self.assertEqual(bound_counter.aggregator.current, 5)
307307

308+
@mock.patch("opentelemetry.sdk.metrics.logger")
309+
def test_add_non_decreasing_int_error(self, logger_mock):
310+
meter = metrics.MeterProvider().get_meter(__name__)
311+
metric = metrics.Counter("name", "desc", "unit", int, meter, ("key",))
312+
labels = {"key": "value"}
313+
bound_counter = metric.bind(labels)
314+
metric.add(3, labels)
315+
metric.add(0, labels)
316+
metric.add(-1, labels)
317+
self.assertEqual(bound_counter.aggregator.current, 3)
318+
self.assertEqual(logger_mock.warning.call_count, 1)
319+
320+
@mock.patch("opentelemetry.sdk.metrics.logger")
321+
def test_add_non_decreasing_float_error(self, logger_mock):
322+
meter = metrics.MeterProvider().get_meter(__name__)
323+
metric = metrics.Counter(
324+
"name", "desc", "unit", float, meter, ("key",)
325+
)
326+
labels = {"key": "value"}
327+
bound_counter = metric.bind(labels)
328+
metric.add(3.3, labels)
329+
metric.add(0.0, labels)
330+
metric.add(0.1, labels)
331+
metric.add(-0.1, labels)
332+
self.assertEqual(bound_counter.aggregator.current, 3.4)
333+
self.assertEqual(logger_mock.warning.call_count, 1)
334+
308335

309336
class TestUpDownCounter(unittest.TestCase):
310-
def test_add(self):
337+
@mock.patch("opentelemetry.sdk.metrics.logger")
338+
def test_add(self, logger_mock):
311339
meter = metrics.MeterProvider().get_meter(__name__)
312340
metric = metrics.UpDownCounter(
313341
"name", "desc", "unit", int, meter, ("key",)
@@ -322,6 +350,7 @@ def test_add(self):
322350
metric.add(-3, labels)
323351
metric.add(-1, labels)
324352
self.assertEqual(bound_counter.aggregator.current, 1)
353+
self.assertEqual(logger_mock.warning.call_count, 0)
325354

326355

327356
class TestValueRecorder(unittest.TestCase):

0 commit comments

Comments
 (0)