Skip to content

Commit 222fb9b

Browse files
a-recknagelArne Caratti
authored and
Arne Caratti
committed
merged recent main and resolved conflicts, and added changelog info
2 parents de1d49b + d03a622 commit 222fb9b

26 files changed

+791
-117
lines changed

CHANGELOG.md

+10-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## Unreleased
9-
9+
- Retain meaningful logrecord attributes and apply log-message
10+
formatting ([#3673](https://github.com/open-telemetry/opentelemetry-python/pull/3673))
11+
- Logs: ObservedTimestamp field is missing in console exporter output
12+
([#3564](https://github.com/open-telemetry/opentelemetry-python/pull/3564))
13+
- Fix explicit bucket histogram aggregation
14+
([#3429](https://github.com/open-telemetry/opentelemetry-python/pull/3429))
15+
- Add `code.lineno`, `code.function` and `code.filepath` to all logs
16+
([#3645](https://github.com/open-telemetry/opentelemetry-python/pull/3645))
17+
- Add Synchronous Gauge instrument
18+
([#3462](https://github.com/open-telemetry/opentelemetry-python/pull/3462))
1019
- Drop support for 3.7
1120
([#3668](https://github.com/open-telemetry/opentelemetry-python/pull/3668))
1221
- Include key in attribute sequence warning

docs/conf.py

+11
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,14 @@
178178
"scm_raw_web": (scm_raw_web + "/%s", "scm_raw_web"),
179179
"scm_web": (scm_web + "/%s", "scm_web"),
180180
}
181+
182+
183+
def on_missing_reference(app, env, node, contnode):
184+
# FIXME Remove when opentelemetry.metrics._Gauge is renamed to
185+
# opentelemetry.metrics.Gauge
186+
if node["reftarget"] == "opentelemetry.metrics.Gauge":
187+
return contnode
188+
189+
190+
def setup(app):
191+
app.connect("missing-reference", on_missing_reference)

docs/getting_started/metrics_example.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,10 @@ def observable_gauge_func(options: CallbackOptions) -> Iterable[Observation]:
7575
histogram.record(99.9)
7676

7777
# Async Gauge
78-
gauge = meter.create_observable_gauge("gauge", [observable_gauge_func])
78+
observable_gauge = meter.create_observable_gauge(
79+
"observable_gauge", [observable_gauge_func]
80+
)
81+
82+
# Sync Gauge
83+
gauge = meter.create_gauge("gauge")
84+
gauge.set(1)

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

+10
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,15 @@
5353
CallbackOptions,
5454
CallbackT,
5555
Counter,
56+
)
57+
from opentelemetry.metrics._internal.instrument import Gauge as _Gauge
58+
from opentelemetry.metrics._internal.instrument import (
5659
Histogram,
5760
Instrument,
5861
NoOpCounter,
62+
)
63+
from opentelemetry.metrics._internal.instrument import NoOpGauge as _NoOpGauge
64+
from opentelemetry.metrics._internal.instrument import (
5965
NoOpHistogram,
6066
NoOpObservableCounter,
6167
NoOpObservableGauge,
@@ -74,6 +80,8 @@
7480
Synchronous,
7581
Asynchronous,
7682
CallbackOptions,
83+
_Gauge,
84+
_NoOpGauge,
7785
get_meter_provider,
7886
get_meter,
7987
Histogram,
@@ -103,6 +111,8 @@
103111
"NoOpMeterProvider",
104112
"Meter",
105113
"Counter",
114+
"_Gauge",
115+
"_NoOpGauge",
106116
"NoOpCounter",
107117
"UpDownCounter",
108118
"NoOpUpDownCounter",

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

+54
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@
5151
from opentelemetry.metrics._internal.instrument import (
5252
CallbackT,
5353
Counter,
54+
Gauge,
5455
Histogram,
5556
NoOpCounter,
57+
NoOpGauge,
5658
NoOpHistogram,
5759
NoOpObservableCounter,
5860
NoOpObservableGauge,
@@ -63,6 +65,7 @@
6365
ObservableUpDownCounter,
6466
UpDownCounter,
6567
_ProxyCounter,
68+
_ProxyGauge,
6669
_ProxyHistogram,
6770
_ProxyObservableCounter,
6871
_ProxyObservableGauge,
@@ -79,6 +82,7 @@
7982
_ProxyInstrumentT = Union[
8083
_ProxyCounter,
8184
_ProxyHistogram,
85+
_ProxyGauge,
8286
_ProxyObservableCounter,
8387
_ProxyObservableGauge,
8488
_ProxyObservableUpDownCounter,
@@ -381,6 +385,22 @@ def create_histogram(
381385
description: A description for this instrument and what it measures.
382386
"""
383387

388+
@abstractmethod
389+
def create_gauge(
390+
self,
391+
name: str,
392+
unit: str = "",
393+
description: str = "",
394+
) -> Gauge:
395+
"""Creates a ``Gauge`` instrument
396+
397+
Args:
398+
name: The name of the instrument to be created
399+
unit: The unit for observations this instrument reports. For
400+
example, ``By`` for bytes. UCUM units are recommended.
401+
description: A description for this instrument and what it measures.
402+
"""
403+
384404
@abstractmethod
385405
def create_observable_gauge(
386406
self,
@@ -512,6 +532,19 @@ def create_histogram(
512532
self._instruments.append(proxy)
513533
return proxy
514534

535+
def create_gauge(
536+
self,
537+
name: str,
538+
unit: str = "",
539+
description: str = "",
540+
) -> Gauge:
541+
with self._lock:
542+
if self._real_meter:
543+
return self._real_meter.create_gauge(name, unit, description)
544+
proxy = _ProxyGauge(name, unit, description)
545+
self._instruments.append(proxy)
546+
return proxy
547+
515548
def create_observable_gauge(
516549
self,
517550
name: str,
@@ -579,6 +612,27 @@ def create_counter(
579612
)
580613
return NoOpCounter(name, unit=unit, description=description)
581614

615+
def create_gauge(
616+
self,
617+
name: str,
618+
unit: str = "",
619+
description: str = "",
620+
) -> Gauge:
621+
"""Returns a no-op Gauge."""
622+
super().create_gauge(name, unit=unit, description=description)
623+
if self._is_instrument_registered(name, NoOpGauge, unit, description)[
624+
0
625+
]:
626+
_logger.warning(
627+
"An instrument with name %s, type %s, unit %s and "
628+
"description %s has been created already.",
629+
name,
630+
Gauge.__name__,
631+
unit,
632+
description,
633+
)
634+
return NoOpGauge(name, unit=unit, description=description)
635+
582636
def create_up_down_counter(
583637
self,
584638
name: str,

opentelemetry-api/src/opentelemetry/metrics/_internal/instrument.py

+47
Original file line numberDiff line numberDiff line change
@@ -396,3 +396,50 @@ def _create_real_instrument(
396396
return meter.create_observable_gauge(
397397
self._name, self._callbacks, self._unit, self._description
398398
)
399+
400+
401+
class Gauge(Synchronous):
402+
"""A Gauge is a synchronous `Instrument` which can be used to record non-additive values as they occur."""
403+
404+
@abstractmethod
405+
def set(
406+
self,
407+
amount: Union[int, float],
408+
attributes: Optional[Attributes] = None,
409+
) -> None:
410+
pass
411+
412+
413+
class NoOpGauge(Gauge):
414+
"""No-op implementation of ``Gauge``."""
415+
416+
def __init__(
417+
self,
418+
name: str,
419+
unit: str = "",
420+
description: str = "",
421+
) -> None:
422+
super().__init__(name, unit=unit, description=description)
423+
424+
def set(
425+
self,
426+
amount: Union[int, float],
427+
attributes: Optional[Attributes] = None,
428+
) -> None:
429+
return super().set(amount, attributes=attributes)
430+
431+
432+
class _ProxyGauge(
433+
_ProxyInstrument[Gauge],
434+
Gauge,
435+
):
436+
def set(
437+
self,
438+
amount: Union[int, float],
439+
attributes: Optional[Attributes] = None,
440+
) -> None:
441+
if self._real_instrument:
442+
self._real_instrument.set(amount, attributes)
443+
444+
def _create_real_instrument(self, meter: "metrics.Meter") -> Gauge:
445+
return meter.create_gauge(self._name, self._unit, self._description)

opentelemetry-api/tests/metrics/test_instruments.py

+45
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
ObservableGauge,
3030
ObservableUpDownCounter,
3131
UpDownCounter,
32+
_Gauge,
3233
)
3334

3435
# FIXME Test that the instrument methods can be called concurrently safely.
@@ -277,6 +278,50 @@ def test_histogram_record_method(self):
277278
self.assertIsNone(NoOpHistogram("name").record(1))
278279

279280

281+
class TestGauge(TestCase):
282+
def test_create_gauge(self):
283+
"""
284+
Test that the Gauge can be created with create_gauge.
285+
"""
286+
287+
self.assertTrue(
288+
isinstance(NoOpMeter("name").create_gauge("name"), _Gauge)
289+
)
290+
291+
def test_api_gauge_abstract(self):
292+
"""
293+
Test that the API Gauge is an abstract class.
294+
"""
295+
296+
self.assertTrue(isabstract(_Gauge))
297+
298+
def test_create_gauge_api(self):
299+
"""
300+
Test that the API for creating a gauge accepts the name of the instrument.
301+
Test that the API for creating a gauge accepts a sequence of callbacks.
302+
Test that the API for creating a gauge accepts the unit of the instrument.
303+
Test that the API for creating a gauge accepts the description of the instrument
304+
"""
305+
306+
create_gauge_signature = signature(Meter.create_gauge)
307+
self.assertIn("name", create_gauge_signature.parameters.keys())
308+
self.assertIs(
309+
create_gauge_signature.parameters["name"].default,
310+
Signature.empty,
311+
)
312+
create_gauge_signature = signature(Meter.create_gauge)
313+
create_gauge_signature = signature(Meter.create_gauge)
314+
self.assertIn("unit", create_gauge_signature.parameters.keys())
315+
self.assertIs(create_gauge_signature.parameters["unit"].default, "")
316+
317+
create_gauge_signature = signature(Meter.create_gauge)
318+
self.assertIn("description", create_gauge_signature.parameters.keys())
319+
self.assertIs(
320+
create_gauge_signature.parameters["description"].default,
321+
"",
322+
)
323+
324+
280325
class TestObservableGauge(TestCase):
281326
def test_create_observable_gauge(self):
282327
"""

opentelemetry-api/tests/metrics/test_meter.py

+13
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def create_observable_counter(
4141
def create_histogram(self, name, unit="", description=""):
4242
super().create_histogram(name, unit=unit, description=description)
4343

44+
def create_gauge(self, name, unit="", description=""):
45+
super().create_gauge(name, unit=unit, description=description)
46+
4447
def create_observable_gauge(self, name, callback, unit="", description=""):
4548
super().create_observable_gauge(
4649
name, callback, unit=unit, description=description
@@ -64,6 +67,7 @@ def test_repeated_instrument_names(self):
6467
test_meter.create_up_down_counter("up_down_counter")
6568
test_meter.create_observable_counter("observable_counter", Mock())
6669
test_meter.create_histogram("histogram")
70+
test_meter.create_gauge("gauge")
6771
test_meter.create_observable_gauge("observable_gauge", Mock())
6872
test_meter.create_observable_up_down_counter(
6973
"observable_up_down_counter", Mock()
@@ -75,6 +79,7 @@ def test_repeated_instrument_names(self):
7579
"counter",
7680
"up_down_counter",
7781
"histogram",
82+
"gauge",
7883
]:
7984
with self.assertLogs(level=WARNING):
8085
getattr(test_meter, f"create_{instrument_name}")(
@@ -123,6 +128,14 @@ def test_create_histogram(self):
123128
self.assertTrue(hasattr(Meter, "create_histogram"))
124129
self.assertTrue(Meter.create_histogram.__isabstractmethod__)
125130

131+
def test_create_gauge(self):
132+
"""
133+
Test that the meter provides a function to create a new Gauge
134+
"""
135+
136+
self.assertTrue(hasattr(Meter, "create_gauge"))
137+
self.assertTrue(Meter.create_gauge.__isabstractmethod__)
138+
126139
def test_create_observable_gauge(self):
127140
"""
128141
Test that the meter provides a function to create a new ObservableGauge

0 commit comments

Comments
 (0)