Skip to content

Commit 11467c4

Browse files
authored
api: Implement "named" meters + Remove "Batcher" from Meter constructor (open-telemetry#431)
Implements open-telemetry#221. Also fixes open-telemetry#394. Stateful.py and stateless.py in metrics example folder are not changed to use the new loader in anticipation of open-telemetry#422 being merged first and removing them. Lastly, moves InstrumentationInfo from trace.py in the sdk to utils.
1 parent c2ba065 commit 11467c4

File tree

22 files changed

+321
-248
lines changed

22 files changed

+321
-248
lines changed

README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,14 @@ with tracer.start_as_current_span('foo'):
7070

7171
```python
7272
from opentelemetry import metrics
73-
from opentelemetry.sdk.metrics import Counter, Meter
73+
from opentelemetry.sdk.metrics import Counter, MeterProvider
7474
from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter
75+
from opentelemetry.sdk.metrics.export.controller import PushController
7576

76-
metrics.set_preferred_meter_implementation(lambda T: Meter())
77-
meter = metrics.meter()
77+
metrics.set_preferred_meter_provider_implementation(lambda _: MeterProvider())
78+
meter = metrics.get_meter(__name__)
7879
exporter = ConsoleMetricsExporter()
80+
controller = PushController(meter, exporter, 5)
7981

8082
counter = meter.create_metric(
8183
"available memory",
@@ -89,9 +91,6 @@ counter = meter.create_metric(
8991
label_values = ("staging",)
9092
counter_handle = counter.get_handle(label_values)
9193
counter_handle.add(100)
92-
93-
exporter.export([(counter, label_values)])
94-
exporter.shutdown()
9594
```
9695

9796
See the [API documentation](https://open-telemetry.github.io/opentelemetry-python/) for more detail, and the [examples folder](./examples) for a more sample code.

docs/opentelemetry.sdk.metrics.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Submodules
88

99
opentelemetry.sdk.metrics.export.aggregate
1010
opentelemetry.sdk.metrics.export.batcher
11+
opentelemetry.sdk.util.instrumentation
1112

1213
.. automodule:: opentelemetry.sdk.metrics
1314
:members:

docs/opentelemetry.sdk.trace.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Submodules
77
.. toctree::
88

99
opentelemetry.sdk.trace.export
10+
opentelemetry.sdk.util.instrumentation
1011

1112
.. automodule:: opentelemetry.sdk.trace
1213
:members:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
opentelemetry.sdk.util.instrumentation
2+
==========================================
3+
4+
.. automodule:: opentelemetry.sdk.util.instrumentation

examples/metrics/prometheus.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121

2222
from opentelemetry import metrics
2323
from opentelemetry.ext.prometheus import PrometheusMetricsExporter
24-
from opentelemetry.sdk.metrics import Counter, Meter
24+
from opentelemetry.sdk.metrics import Counter, MeterProvider
2525
from opentelemetry.sdk.metrics.export.controller import PushController
2626

2727
# Start Prometheus client
2828
start_http_server(port=8000, addr="localhost")
2929

3030
# Meter is responsible for creating and recording metrics
31-
metrics.set_preferred_meter_implementation(lambda _: Meter())
32-
meter = metrics.meter()
31+
metrics.set_preferred_meter_provider_implementation(lambda _: MeterProvider())
32+
meter = metrics.get_meter(__name__)
3333
# exporter to export metrics to Prometheus
3434
prefix = "MyAppPrefix"
3535
exporter = PrometheusMetricsExporter(prefix)

examples/metrics/record.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019, OpenTelemetry Authors
1+
# Copyright 2020, OpenTelemetry Authors
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -19,13 +19,15 @@
1919
import time
2020

2121
from opentelemetry import metrics
22-
from opentelemetry.sdk.metrics import Counter, Meter
22+
from opentelemetry.sdk.metrics import Counter, MeterProvider
2323
from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter
2424
from opentelemetry.sdk.metrics.export.controller import PushController
2525

26+
# The preferred tracer implementation must be set, as the opentelemetry-api
27+
# defines the interface with a no-op implementation.
28+
metrics.set_preferred_meter_provider_implementation(lambda _: MeterProvider())
2629
# Meter is responsible for creating and recording metrics
27-
metrics.set_preferred_meter_implementation(lambda _: Meter())
28-
meter = metrics.meter()
30+
meter = metrics.get_meter(__name__)
2931
# exporter to export metrics to the console
3032
exporter = ConsoleMetricsExporter()
3133
# controller collects metrics created from meter and exports it via the

examples/metrics/simple_example.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019, OpenTelemetry Authors
1+
# Copyright 2020, OpenTelemetry Authors
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -23,9 +23,8 @@
2323
import time
2424

2525
from opentelemetry import metrics
26-
from opentelemetry.sdk.metrics import Counter, Measure, Meter
26+
from opentelemetry.sdk.metrics import Counter, Measure, MeterProvider
2727
from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter
28-
from opentelemetry.sdk.metrics.export.batcher import UngroupedBatcher
2928
from opentelemetry.sdk.metrics.export.controller import PushController
3029

3130
batcher_mode = "stateful"
@@ -44,18 +43,15 @@ def usage(argv):
4443
usage(sys.argv)
4544
sys.exit(1)
4645

47-
# Batcher used to collect all created metrics from meter ready for exporting
48-
# Pass in True/False to indicate whether the batcher is stateful.
49-
# True indicates the batcher computes checkpoints from over the process
50-
# lifetime.
51-
# False indicates the batcher computes checkpoints which describe the updates
52-
# of a single collection period (deltas)
53-
batcher = UngroupedBatcher(batcher_mode == "stateful")
54-
55-
# If a batcher is not provided, a default batcher is used
5646
# Meter is responsible for creating and recording metrics
57-
metrics.set_preferred_meter_implementation(lambda _: Meter(batcher))
58-
meter = metrics.meter()
47+
metrics.set_preferred_meter_provider_implementation(lambda _: MeterProvider())
48+
49+
# Meter's namespace corresponds to the string passed as the first argument Pass
50+
# in True/False to indicate whether the batcher is stateful. True indicates the
51+
# batcher computes checkpoints from over the process lifetime. False indicates
52+
# the batcher computes checkpoints which describe the updates of a single
53+
# collection period (deltas)
54+
meter = metrics.get_meter(__name__, batcher_mode == "stateful")
5955

6056
# Exporter to export metrics to the console
6157
exporter = ConsoleMetricsExporter()

examples/opentelemetry-example-app/src/opentelemetry_example_app/metrics_example.py

Lines changed: 0 additions & 50 deletions
This file was deleted.

ext/opentelemetry-ext-prometheus/tests/test_prometheus_exporter.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
class TestPrometheusMetricExporter(unittest.TestCase):
3030
def setUp(self):
31-
self._meter = metrics.Meter()
31+
self._meter = metrics.MeterProvider().get_meter(__name__)
3232
self._test_metric = self._meter.create_metric(
3333
"testname",
3434
"testdesc",
@@ -74,7 +74,7 @@ def test_export(self):
7474
self.assertIs(result, MetricsExportResult.SUCCESS)
7575

7676
def test_counter_to_prometheus(self):
77-
meter = metrics.Meter()
77+
meter = metrics.MeterProvider().get_meter(__name__)
7878
metric = meter.create_metric(
7979
"test@name",
8080
"testdesc",
@@ -111,7 +111,7 @@ def test_counter_to_prometheus(self):
111111

112112
def test_invalid_metric(self):
113113

114-
meter = metrics.Meter()
114+
meter = metrics.MeterProvider().get_meter(__name__)
115115
metric = meter.create_metric(
116116
"tesname", "testdesc", "unit", int, TestMetric
117117
)

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

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2019, OpenTelemetry Authors
1+
# Copyright 2020, OpenTelemetry Authors
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -27,10 +27,13 @@
2727
2828
"""
2929
import abc
30+
import logging
3031
from typing import Callable, Dict, Optional, Sequence, Tuple, Type, TypeVar
3132

3233
from opentelemetry.util import loader
3334

35+
logger = logging.getLogger(__name__)
36+
3437
ValueT = TypeVar("ValueT", int, float)
3538

3639

@@ -224,6 +227,56 @@ def record(self, value: ValueT, label_set: LabelSet) -> None:
224227
"""
225228

226229

230+
class MeterProvider(abc.ABC):
231+
@abc.abstractmethod
232+
def get_meter(
233+
self,
234+
instrumenting_module_name: str,
235+
stateful: bool = True,
236+
instrumenting_library_version: str = "",
237+
) -> "Meter":
238+
"""Returns a `Meter` for use by the given instrumentation library.
239+
240+
This function may return different `Meter` types (e.g. a no-op meter
241+
vs. a functional meter).
242+
243+
Args:
244+
instrumenting_module_name: The name of the instrumenting module
245+
(usually just ``__name__``).
246+
247+
This should *not* be the name of the module that is
248+
instrumented but the name of the module doing the instrumentation.
249+
E.g., instead of ``"requests"``, use
250+
``"opentelemetry.ext.http_requests"``.
251+
252+
stateful: True/False to indicate whether the meter will be
253+
stateful. True indicates the meter computes checkpoints
254+
from over the process lifetime. False indicates the meter
255+
computes checkpoints which describe the updates of a single
256+
collection period (deltas).
257+
258+
instrumenting_library_version: Optional. The version string of the
259+
instrumenting library. Usually this should be the same as
260+
``pkg_resources.get_distribution(instrumenting_library_name).version``.
261+
"""
262+
263+
264+
class DefaultMeterProvider(MeterProvider):
265+
"""The default MeterProvider, used when no implementation is available.
266+
267+
All operations are no-op.
268+
"""
269+
270+
def get_meter(
271+
self,
272+
instrumenting_module_name: str,
273+
stateful: bool = True,
274+
instrumenting_library_version: str = "",
275+
) -> "Meter":
276+
# pylint:disable=no-self-use,unused-argument
277+
return DefaultMeter()
278+
279+
227280
MetricT = TypeVar("MetricT", Counter, Gauge, Measure)
228281

229282

@@ -322,45 +375,69 @@ def get_label_set(self, labels: Dict[str, str]) -> "LabelSet":
322375
# Once https://github.com/python/mypy/issues/7092 is resolved,
323376
# the following type definition should be replaced with
324377
# from opentelemetry.util.loader import ImplementationFactory
325-
ImplementationFactory = Callable[[Type[Meter]], Optional[Meter]]
326-
327-
_METER = None
328-
_METER_FACTORY = None
378+
ImplementationFactory = Callable[
379+
[Type[MeterProvider]], Optional[MeterProvider]
380+
]
381+
382+
_METER_PROVIDER = None
383+
_METER_PROVIDER_FACTORY = None
384+
385+
386+
def get_meter(
387+
instrumenting_module_name: str,
388+
stateful: bool = True,
389+
instrumenting_library_version: str = "",
390+
) -> "Meter":
391+
"""Returns a `Meter` for use by the given instrumentation library.
392+
This function is a convenience wrapper for
393+
opentelemetry.metrics.meter_provider().get_meter
394+
"""
395+
return meter_provider().get_meter(
396+
instrumenting_module_name, stateful, instrumenting_library_version
397+
)
329398

330399

331-
def meter() -> Meter:
332-
"""Gets the current global :class:`~.Meter` object.
400+
def meter_provider() -> MeterProvider:
401+
"""Gets the current global :class:`~.MeterProvider` object.
333402
334403
If there isn't one set yet, a default will be loaded.
335404
"""
336-
global _METER, _METER_FACTORY # pylint:disable=global-statement
405+
global _METER_PROVIDER, _METER_PROVIDER_FACTORY # pylint:disable=global-statement
337406

338-
if _METER is None:
407+
if _METER_PROVIDER is None:
339408
# pylint:disable=protected-access
340409
try:
341-
_METER = loader._load_impl(Meter, _METER_FACTORY) # type: ignore
410+
_METER_PROVIDER = loader._load_impl(
411+
MeterProvider, _METER_PROVIDER_FACTORY # type: ignore
412+
)
342413
except TypeError:
343414
# if we raised an exception trying to instantiate an
344-
# abstract class, default to no-op tracer impl
345-
_METER = DefaultMeter()
346-
del _METER_FACTORY
415+
# abstract class, default to no-op meter impl
416+
logger.warning(
417+
"Unable to instantiate MeterProvider from meter provider factory.",
418+
exc_info=True,
419+
)
420+
_METER_PROVIDER = DefaultMeterProvider()
421+
_METER_PROVIDER_FACTORY = None
347422

348-
return _METER
423+
return _METER_PROVIDER
349424

350425

351-
def set_preferred_meter_implementation(factory: ImplementationFactory) -> None:
352-
"""Set the factory to be used to create the meter.
426+
def set_preferred_meter_provider_implementation(
427+
factory: ImplementationFactory,
428+
) -> None:
429+
"""Set the factory to be used to create the meter provider.
353430
354431
See :mod:`opentelemetry.util.loader` for details.
355432
356433
This function may not be called after a meter is already loaded.
357434
358435
Args:
359-
factory: Callback that should create a new :class:`Meter` instance.
436+
factory: Callback that should create a new :class:`MeterProvider` instance.
360437
"""
361-
global _METER, _METER_FACTORY # pylint:disable=global-statement
438+
global _METER_PROVIDER_FACTORY # pylint:disable=global-statement
362439

363-
if _METER:
364-
raise RuntimeError("Meter already loaded.")
440+
if _METER_PROVIDER:
441+
raise RuntimeError("MeterProvider already loaded.")
365442

366-
_METER_FACTORY = factory
443+
_METER_PROVIDER_FACTORY = factory

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
# Copyright 2020, OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
114
import time
215

316
# Since we want API users to be able to provide timestamps,

0 commit comments

Comments
 (0)