Skip to content

Commit 9d39f5f

Browse files
ocelotlabhinav283
authored andcommitted
Fix version of Flask dependency werkzeug (open-telemetry#1980)
Feature/add new process metrics (open-telemetry#1948) Using new cloud resource id attribute (open-telemetry#1976) Fixes open-telemetry#1970,remove pkg resources and use importlib_metadata
1 parent e318c94 commit 9d39f5f

File tree

20 files changed

+272
-114
lines changed

20 files changed

+272
-114
lines changed

CHANGELOG.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
11+
### Added
12+
- `opentelemetry-instrumentation-system-metrics` Add support for collecting process metrics
13+
([#1948](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1948))
14+
15+
### Fixed
16+
17+
- Fix version of Flask dependency `werkzeug`
18+
([#1980](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1980))
19+
- `opentelemetry-instrumentation`, `opentelemetry-instrumentation-aiohttp-client` Use importlib-metadata for entry points instead of pkg_resources
20+
([#2124](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2124))
21+
1022
## Version 1.20.0/0.41b0 (2023-09-01)
1123

1224
### Fixed
@@ -352,7 +364,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
352364
- `opentelemetry-instrumentation-sqlalchemy` Added span for the connection phase ([#1133](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/1133))
353365
- Add metric instrumentation in asgi
354366
([#1197](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1197))
355-
- Add metric instumentation for flask
367+
- Add metric instrumentation for flask
356368
([#1186](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1186))
357369
- Add a test for asgi using NoOpTracerProvider
358370
([#1367](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1367))

instrumentation/opentelemetry-instrumentation-aiohttp-client/tests/test_aiohttp_client_integration.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222

2323
import aiohttp
2424
import aiohttp.test_utils
25+
import importlib_metadata
2526
import yarl
2627
from http_server_mock import HttpServerMock
27-
from pkg_resources import iter_entry_points
2828

2929
from opentelemetry import context
3030
from opentelemetry import trace as trace_api
@@ -574,8 +574,8 @@ def response_hook(
574574

575575
class TestLoadingAioHttpInstrumentor(unittest.TestCase):
576576
def test_loading_instrumentor(self):
577-
entry_points = iter_entry_points(
578-
"opentelemetry_instrumentor", "aiohttp-client"
577+
entry_points = importlib_metadata.entry_points(
578+
group="opentelemetry_instrumentor", name="aiohttp-client"
579579
)
580580

581581
instrumentor = next(entry_points).load()()

instrumentation/opentelemetry-instrumentation-flask/pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ dependencies = [
3636
[project.optional-dependencies]
3737
instruments = [
3838
"flask >= 1.0, < 3.0",
39+
"werkzeug < 3.0.0"
3940
]
4041
test = [
4142
"opentelemetry-instrumentation-flask[instruments]",

instrumentation/opentelemetry-instrumentation-sklearn/src/opentelemetry/instrumentation/sklearn/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
).instrument()
3636
3737
38-
Model intrumentation example:
38+
Model instrumentation example:
3939
4040
.. code-block:: python
4141
@@ -291,7 +291,7 @@ class descendent) is being instrumented with opentelemetry. Within a
291291
SklearnInstrumentor(packages=packages).instrument()
292292
293293
294-
Model intrumentation example:
294+
Model instrumentation example:
295295
296296
.. code-block:: python
297297

instrumentation/opentelemetry-instrumentation-system-metrics/src/opentelemetry/instrumentation/system_metrics/__init__.py

+65
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
"system.thread_count": None
3737
"process.runtime.memory": ["rss", "vms"],
3838
"process.runtime.cpu.time": ["user", "system"],
39+
"process.runtime.gc_count": None,
40+
"process.runtime.thread_count": None,
41+
"process.runtime.cpu.utilization": None,
42+
"process.runtime.context_switches": ["involuntary", "voluntary"],
3943
}
4044
4145
Usage
@@ -63,6 +67,7 @@
6367
"system.network.io": ["transmit", "receive"],
6468
"process.runtime.memory": ["rss", "vms"],
6569
"process.runtime.cpu.time": ["user", "system"],
70+
"process.runtime.context_switches": ["involuntary", "voluntary"],
6671
}
6772
SystemMetricsInstrumentor(config=configuration).instrument()
6873
@@ -105,6 +110,9 @@
105110
"process.runtime.memory": ["rss", "vms"],
106111
"process.runtime.cpu.time": ["user", "system"],
107112
"process.runtime.gc_count": None,
113+
"process.runtime.thread_count": None,
114+
"process.runtime.cpu.utilization": None,
115+
"process.runtime.context_switches": ["involuntary", "voluntary"],
108116
}
109117

110118

@@ -150,6 +158,9 @@ def __init__(
150158
self._runtime_memory_labels = self._labels.copy()
151159
self._runtime_cpu_time_labels = self._labels.copy()
152160
self._runtime_gc_count_labels = self._labels.copy()
161+
self._runtime_thread_count_labels = self._labels.copy()
162+
self._runtime_cpu_utilization_labels = self._labels.copy()
163+
self._runtime_context_switches_labels = self._labels.copy()
153164

154165
def instrumentation_dependencies(self) -> Collection[str]:
155166
return _instruments
@@ -347,6 +358,29 @@ def _instrument(self, **kwargs):
347358
unit="bytes",
348359
)
349360

361+
if "process.runtime.thread_count" in self._config:
362+
self._meter.create_observable_up_down_counter(
363+
name=f"process.runtime.{self._python_implementation}.thread_count",
364+
callbacks=[self._get_runtime_thread_count],
365+
description="Runtime active threads count",
366+
)
367+
368+
if "process.runtime.cpu.utilization" in self._config:
369+
self._meter.create_observable_gauge(
370+
name=f"process.runtime.{self._python_implementation}.cpu.utilization",
371+
callbacks=[self._get_runtime_cpu_utilization],
372+
description="Runtime CPU utilization",
373+
unit="1",
374+
)
375+
376+
if "process.runtime.context_switches" in self._config:
377+
self._meter.create_observable_counter(
378+
name=f"process.runtime.{self._python_implementation}.context_switches",
379+
callbacks=[self._get_runtime_context_switches],
380+
description="Runtime context switches",
381+
unit="switches",
382+
)
383+
350384
def _uninstrument(self, **__):
351385
pass
352386

@@ -646,3 +680,34 @@ def _get_runtime_gc_count(
646680
for index, count in enumerate(gc.get_count()):
647681
self._runtime_gc_count_labels["count"] = str(index)
648682
yield Observation(count, self._runtime_gc_count_labels.copy())
683+
684+
def _get_runtime_thread_count(
685+
self, options: CallbackOptions
686+
) -> Iterable[Observation]:
687+
"""Observer callback for runtime active thread count"""
688+
yield Observation(
689+
self._proc.num_threads(), self._runtime_thread_count_labels.copy()
690+
)
691+
692+
def _get_runtime_cpu_utilization(
693+
self, options: CallbackOptions
694+
) -> Iterable[Observation]:
695+
"""Observer callback for runtime CPU utilization"""
696+
proc_cpu_percent = self._proc.cpu_percent()
697+
yield Observation(
698+
proc_cpu_percent,
699+
self._runtime_cpu_utilization_labels.copy(),
700+
)
701+
702+
def _get_runtime_context_switches(
703+
self, options: CallbackOptions
704+
) -> Iterable[Observation]:
705+
"""Observer callback for runtime context switches"""
706+
ctx_switches = self._proc.num_ctx_switches()
707+
for metric in self._config["process.runtime.context_switches"]:
708+
if hasattr(ctx_switches, metric):
709+
self._runtime_context_switches_labels["type"] = metric
710+
yield Observation(
711+
getattr(ctx_switches, metric),
712+
self._runtime_context_switches_labels.copy(),
713+
)

instrumentation/opentelemetry-instrumentation-system-metrics/tests/test_system_metrics.py

+52-8
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
from platform import python_implementation
1919
from unittest import mock
2020

21-
from opentelemetry.instrumentation.system_metrics import (
22-
SystemMetricsInstrumentor,
23-
)
2421
from opentelemetry.sdk.metrics import MeterProvider
2522
from opentelemetry.sdk.metrics.export import InMemoryMetricReader
2623
from opentelemetry.test.test_base import TestBase
2724

25+
from opentelemetry.instrumentation.system_metrics import (
26+
SystemMetricsInstrumentor,
27+
)
28+
2829

2930
def _mock_netconnection():
3031
NetConnection = namedtuple(
@@ -96,7 +97,7 @@ def test_system_metrics_instrument(self):
9697
for scope_metrics in resource_metrics.scope_metrics:
9798
for metric in scope_metrics.metrics:
9899
metric_names.append(metric.name)
99-
self.assertEqual(len(metric_names), 18)
100+
self.assertEqual(len(metric_names), 21)
100101

101102
observer_names = [
102103
"system.cpu.time",
@@ -117,6 +118,9 @@ def test_system_metrics_instrument(self):
117118
f"process.runtime.{self.implementation}.memory",
118119
f"process.runtime.{self.implementation}.cpu_time",
119120
f"process.runtime.{self.implementation}.gc_count",
121+
f"process.runtime.{self.implementation}.thread_count",
122+
f"process.runtime.{self.implementation}.context_switches",
123+
f"process.runtime.{self.implementation}.cpu.utilization",
120124
]
121125

122126
for observer in metric_names:
@@ -128,6 +132,9 @@ def test_runtime_metrics_instrument(self):
128132
"process.runtime.memory": ["rss", "vms"],
129133
"process.runtime.cpu.time": ["user", "system"],
130134
"process.runtime.gc_count": None,
135+
"process.runtime.thread_count": None,
136+
"process.runtime.cpu.utilization": None,
137+
"process.runtime.context_switches": ["involuntary", "voluntary"],
131138
}
132139

133140
reader = InMemoryMetricReader()
@@ -140,12 +147,15 @@ def test_runtime_metrics_instrument(self):
140147
for scope_metrics in resource_metrics.scope_metrics:
141148
for metric in scope_metrics.metrics:
142149
metric_names.append(metric.name)
143-
self.assertEqual(len(metric_names), 3)
150+
self.assertEqual(len(metric_names), 6)
144151

145152
observer_names = [
146153
f"process.runtime.{self.implementation}.memory",
147154
f"process.runtime.{self.implementation}.cpu_time",
148155
f"process.runtime.{self.implementation}.gc_count",
156+
f"process.runtime.{self.implementation}.thread_count",
157+
f"process.runtime.{self.implementation}.context_switches",
158+
f"process.runtime.{self.implementation}.cpu.utilization",
149159
]
150160

151161
for observer in metric_names:
@@ -161,9 +171,9 @@ def _assert_metrics(self, observer_name, reader, expected):
161171
for data_point in metric.data.data_points:
162172
for expect in expected:
163173
if (
164-
dict(data_point.attributes)
165-
== expect.attributes
166-
and metric.name == observer_name
174+
dict(data_point.attributes)
175+
== expect.attributes
176+
and metric.name == observer_name
167177
):
168178
self.assertEqual(
169179
data_point.value,
@@ -782,3 +792,37 @@ def test_runtime_get_count(self, mock_gc_get_count):
782792
self._test_metrics(
783793
f"process.runtime.{self.implementation}.gc_count", expected
784794
)
795+
796+
@mock.patch("psutil.Process.num_ctx_switches")
797+
def test_runtime_context_switches(self, mock_process_num_ctx_switches):
798+
PCtxSwitches = namedtuple("PCtxSwitches", ["voluntary", "involuntary"])
799+
800+
mock_process_num_ctx_switches.configure_mock(
801+
**{"return_value": PCtxSwitches(voluntary=1, involuntary=2)}
802+
)
803+
804+
expected = [
805+
_SystemMetricsResult({"type": "voluntary"}, 1),
806+
_SystemMetricsResult({"type": "involuntary"}, 2),
807+
]
808+
self._test_metrics(
809+
f"process.runtime.{self.implementation}.context_switches", expected
810+
)
811+
812+
@mock.patch("psutil.Process.num_threads")
813+
def test_runtime_thread_num(self, mock_process_thread_num):
814+
mock_process_thread_num.configure_mock(**{"return_value": 42})
815+
816+
expected = [_SystemMetricsResult({}, 42)]
817+
self._test_metrics(
818+
f"process.runtime.{self.implementation}.thread_count", expected
819+
)
820+
821+
@mock.patch("psutil.Process.cpu_percent")
822+
def test_runtime_cpu_percent(self, mock_process_cpu_percent):
823+
mock_process_cpu_percent.configure_mock(**{"return_value": 42})
824+
825+
expected = [_SystemMetricsResult({}, 42)]
826+
self._test_metrics(
827+
f"process.runtime.{self.implementation}.cpu.utilization", expected
828+
)

instrumentation/opentelemetry-instrumentation-wsgi/tests/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright The OpenTelemetry Authors
1+
# Copyright The Open Telemetry 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.
@@ -11,7 +11,6 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
import pkg_resources
1514

1615
# IMPORTANT: Only the wsgi module needs this because it is always the first
1716
# package that uses the `{rootdir}/*/tests/` path and gets installed by
@@ -20,4 +19,5 @@
2019
# Naming the tests module as a namespace package ensures that
2120
# relative imports will resolve properly for subsequent test packages,
2221
# as it enables searching for a composite of multiple test modules.
23-
pkg_resources.declare_namespace(__name__)
22+
23+
# No need for additional code here for Python 3.3+

opentelemetry-distro/tests/test_distro.py

+3-5
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515

1616
import os
1717
from unittest import TestCase
18-
19-
from pkg_resources import DistributionNotFound, require
18+
import importlib_metadata
2019

2120
from opentelemetry.distro import OpenTelemetryDistro
2221
from opentelemetry.environment_variables import (
@@ -25,12 +24,11 @@
2524
)
2625
from opentelemetry.sdk.environment_variables import OTEL_EXPORTER_OTLP_PROTOCOL
2726

28-
2927
class TestDistribution(TestCase):
3028
def test_package_available(self):
3129
try:
32-
require(["opentelemetry-distro"])
33-
except DistributionNotFound:
30+
importlib_metadata.distribution("opentelemetry-distro")
31+
except importlib_metadata.PackageNotFoundError:
3432
self.fail("opentelemetry-distro not installed")
3533

3634
def test_default_configuration(self):

opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/__init__.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from re import sub
2222
from shutil import which
2323

24-
from pkg_resources import iter_entry_points
24+
from importlib_metadata import entry_points
2525

2626
from opentelemetry.instrumentation.version import __version__
2727

@@ -50,8 +50,8 @@ def run() -> None:
5050

5151
argument_otel_environment_variable = {}
5252

53-
for entry_point in iter_entry_points(
54-
"opentelemetry_environment_variables"
53+
for entry_point in entry_points(
54+
group="opentelemetry_environment_variables"
5555
):
5656
environment_variable_module = entry_point.load()
5757

0 commit comments

Comments
 (0)