Skip to content

Commit 6f99ac8

Browse files
committed
Allow distro and configurator selection
1 parent dadcd01 commit 6f99ac8

File tree

7 files changed

+461
-91
lines changed

7 files changed

+461
-91
lines changed

CHANGELOG.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3737
([#1810](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1810))
3838
- `opentelemetry-instrumentation-urllib3` Add support for urllib3 version 2
3939
([#1879](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1879))
40+
- Add optional distro and configurator selection for auto-instrumentation
41+
([#1823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1823))
4042

4143
## Version 1.18.0/0.39b0 (2023-05-10)
4244

4345
- `opentelemetry-instrumentation-system-metrics` Add `process.` prefix to `runtime.memory`, `runtime.cpu.time`, and `runtime.gc_count`. Change `runtime.memory` from count to UpDownCounter. ([#1735](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1735))
4446
- Add request and response hooks for GRPC instrumentation (client only)
4547
([#1706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1706))
4648
- Fix memory leak in SQLAlchemy instrumentation where disposed `Engine` does not get garbage collected
47-
([#1771](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1771)
49+
([#1771](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1771))
4850
- `opentelemetry-instrumentation-pymemcache` Update instrumentation to support pymemcache >4
4951
([#1764](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1764))
5052
- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions of confluent_kafka
@@ -86,7 +88,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8688

8789
### Changed
8890
- Update HTTP server/client instrumentation span names to comply with spec
89-
([#1759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1759)
91+
([#1759](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1759))
9092

9193
## Version 1.17.0/0.38b0 (2023-03-22)
9294

opentelemetry-instrumentation/README.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ This package provides a couple of commands that help automatically instruments a
1818

1919
.. note::
2020
You need to install a distro package to get auto instrumentation working. The ``opentelemetry-distro``
21-
package contains the default distro and automatically configures some of the common options for users.
21+
package contains the default distro and configurator and automatically configures some of the common options for users.
2222
For more info about ``opentelemetry-distro`` check `here <https://opentelemetry-python.readthedocs.io/en/latest/examples/distro/README.html>`__
2323
::
2424

2525
pip install opentelemetry-distro[otlp]
2626

27+
When creating a custom distro and/or configurator, be sure to add entry points for each under `opentelemetry_distro` and `opentelemetry_configurator` respectfully.
28+
If you have entry points for multiple distros or configurators present in your environment, you should specify the entry point name of the distro and configurator you want to be used via the `OTEL_PYTHON_DISTRO` and `OTEL_PYTHON_CONFIGURATOR` environment variables.
29+
2730

2831
opentelemetry-bootstrap
2932
-----------------------
@@ -58,6 +61,8 @@ The command supports the following configuration options as CLI arguments and en
5861

5962
* ``--traces_exporter`` or ``OTEL_TRACES_EXPORTER``
6063
* ``--metrics_exporter`` or ``OTEL_METRICS_EXPORTER``
64+
* ``--distro`` or ``OTEL_PYTHON_DISTRO``
65+
* ``--configurator`` or ``OTEL_PYTHON_CONFIGURATOR``
6166

6267
Used to specify which trace exporter to use. Can be set to one or more of the well-known exporter
6368
names (see below).
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Copyright The 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.
14+
15+
from logging import getLogger
16+
from os import environ
17+
18+
from pkg_resources import iter_entry_points
19+
20+
from opentelemetry.instrumentation.dependencies import (
21+
get_dist_dependency_conflicts,
22+
)
23+
from opentelemetry.instrumentation.distro import BaseDistro, DefaultDistro
24+
from opentelemetry.instrumentation.environment_variables import (
25+
OTEL_PYTHON_CONFIGURATOR,
26+
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
27+
OTEL_PYTHON_DISTRO,
28+
)
29+
from opentelemetry.instrumentation.version import __version__
30+
31+
_logger = getLogger(__name__)
32+
33+
34+
def _load_distro() -> BaseDistro:
35+
distro_name = environ.get(OTEL_PYTHON_DISTRO, None)
36+
for entry_point in iter_entry_points("opentelemetry_distro"):
37+
try:
38+
# If no distro is specified, use first to come up.
39+
if distro_name is None or distro_name == entry_point.name:
40+
distro = entry_point.load()()
41+
if not isinstance(distro, BaseDistro):
42+
_logger.debug(
43+
"%s is not an OpenTelemetry Distro. Skipping",
44+
entry_point.name,
45+
)
46+
continue
47+
_logger.debug(
48+
"Distribution %s will be configured", entry_point.name
49+
)
50+
return distro
51+
except Exception as exc: # pylint: disable=broad-except
52+
_logger.exception(
53+
"Distribution %s configuration failed", entry_point.name
54+
)
55+
raise exc
56+
return DefaultDistro()
57+
58+
59+
def _load_instrumentors(distro):
60+
package_to_exclude = environ.get(OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, [])
61+
if isinstance(package_to_exclude, str):
62+
package_to_exclude = package_to_exclude.split(",")
63+
# to handle users entering "requests , flask" or "requests, flask" with spaces
64+
package_to_exclude = [x.strip() for x in package_to_exclude]
65+
66+
for entry_point in iter_entry_points("opentelemetry_pre_instrument"):
67+
entry_point.load()()
68+
69+
for entry_point in iter_entry_points("opentelemetry_instrumentor"):
70+
if entry_point.name in package_to_exclude:
71+
_logger.debug(
72+
"Instrumentation skipped for library %s", entry_point.name
73+
)
74+
continue
75+
76+
try:
77+
conflict = get_dist_dependency_conflicts(entry_point.dist)
78+
if conflict:
79+
_logger.debug(
80+
"Skipping instrumentation %s: %s",
81+
entry_point.name,
82+
conflict,
83+
)
84+
continue
85+
86+
# tell instrumentation to not run dep checks again as we already did it above
87+
distro.load_instrumentor(entry_point, skip_dep_check=True)
88+
_logger.debug("Instrumented %s", entry_point.name)
89+
except Exception as exc: # pylint: disable=broad-except
90+
_logger.exception("Instrumenting of %s failed", entry_point.name)
91+
raise exc
92+
93+
for entry_point in iter_entry_points("opentelemetry_post_instrument"):
94+
entry_point.load()()
95+
96+
97+
def _load_configurators():
98+
configurator_name = environ.get(OTEL_PYTHON_CONFIGURATOR, None)
99+
configured = None
100+
for entry_point in iter_entry_points("opentelemetry_configurator"):
101+
if configured is not None:
102+
_logger.warning(
103+
"Configuration of %s not loaded, %s already loaded",
104+
entry_point.name,
105+
configured,
106+
)
107+
continue
108+
try:
109+
if (
110+
configurator_name is None
111+
or configurator_name == entry_point.name
112+
):
113+
entry_point.load()().configure(auto_instrumentation_version=__version__) # type: ignore
114+
configured = entry_point.name
115+
else:
116+
_logger.warning(
117+
"Configuration of %s not loaded because %s is set by %s",
118+
entry_point.name,
119+
configurator_name,
120+
OTEL_PYTHON_CONFIGURATOR,
121+
)
122+
except Exception as exc: # pylint: disable=broad-except
123+
_logger.exception("Configuration of %s failed", entry_point.name)
124+
raise exc

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

+5-88
Original file line numberDiff line numberDiff line change
@@ -16,107 +16,24 @@
1616
from os import environ
1717
from os.path import abspath, dirname, pathsep
1818

19-
from pkg_resources import iter_entry_points
20-
21-
from opentelemetry.instrumentation.dependencies import (
22-
get_dist_dependency_conflicts,
23-
)
24-
from opentelemetry.instrumentation.distro import BaseDistro, DefaultDistro
25-
from opentelemetry.instrumentation.environment_variables import (
26-
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
19+
from opentelemetry.instrumentation.auto_instrumentation._load import (
20+
_load_configurators,
21+
_load_distro,
22+
_load_instrumentors,
2723
)
2824
from opentelemetry.instrumentation.utils import _python_path_without_directory
29-
from opentelemetry.instrumentation.version import __version__
3025

3126
logger = getLogger(__name__)
3227

3328

34-
def _load_distros() -> BaseDistro:
35-
for entry_point in iter_entry_points("opentelemetry_distro"):
36-
try:
37-
distro = entry_point.load()()
38-
if not isinstance(distro, BaseDistro):
39-
logger.debug(
40-
"%s is not an OpenTelemetry Distro. Skipping",
41-
entry_point.name,
42-
)
43-
continue
44-
logger.debug(
45-
"Distribution %s will be configured", entry_point.name
46-
)
47-
return distro
48-
except Exception as exc: # pylint: disable=broad-except
49-
logger.exception(
50-
"Distribution %s configuration failed", entry_point.name
51-
)
52-
raise exc
53-
return DefaultDistro()
54-
55-
56-
def _load_instrumentors(distro):
57-
package_to_exclude = environ.get(OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, [])
58-
if isinstance(package_to_exclude, str):
59-
package_to_exclude = package_to_exclude.split(",")
60-
# to handle users entering "requests , flask" or "requests, flask" with spaces
61-
package_to_exclude = [x.strip() for x in package_to_exclude]
62-
63-
for entry_point in iter_entry_points("opentelemetry_pre_instrument"):
64-
entry_point.load()()
65-
66-
for entry_point in iter_entry_points("opentelemetry_instrumentor"):
67-
if entry_point.name in package_to_exclude:
68-
logger.debug(
69-
"Instrumentation skipped for library %s", entry_point.name
70-
)
71-
continue
72-
73-
try:
74-
conflict = get_dist_dependency_conflicts(entry_point.dist)
75-
if conflict:
76-
logger.debug(
77-
"Skipping instrumentation %s: %s",
78-
entry_point.name,
79-
conflict,
80-
)
81-
continue
82-
83-
# tell instrumentation to not run dep checks again as we already did it above
84-
distro.load_instrumentor(entry_point, skip_dep_check=True)
85-
logger.debug("Instrumented %s", entry_point.name)
86-
except Exception as exc: # pylint: disable=broad-except
87-
logger.exception("Instrumenting of %s failed", entry_point.name)
88-
raise exc
89-
90-
for entry_point in iter_entry_points("opentelemetry_post_instrument"):
91-
entry_point.load()()
92-
93-
94-
def _load_configurators():
95-
configured = None
96-
for entry_point in iter_entry_points("opentelemetry_configurator"):
97-
if configured is not None:
98-
logger.warning(
99-
"Configuration of %s not loaded, %s already loaded",
100-
entry_point.name,
101-
configured,
102-
)
103-
continue
104-
try:
105-
entry_point.load()().configure(auto_instrumentation_version=__version__) # type: ignore
106-
configured = entry_point.name
107-
except Exception as exc: # pylint: disable=broad-except
108-
logger.exception("Configuration of %s failed", entry_point.name)
109-
raise exc
110-
111-
11229
def initialize():
11330
# prevents auto-instrumentation of subprocesses if code execs another python process
11431
environ["PYTHONPATH"] = _python_path_without_directory(
11532
environ["PYTHONPATH"], dirname(abspath(__file__)), pathsep
11633
)
11734

11835
try:
119-
distro = _load_distros()
36+
distro = _load_distro()
12037
distro.configure()
12138
_load_configurators()
12239
_load_instrumentors(distro)

opentelemetry-instrumentation/src/opentelemetry/instrumentation/environment_variables.py

+10
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,13 @@
1616
"""
1717
.. envvar:: OTEL_PYTHON_DISABLED_INSTRUMENTATIONS
1818
"""
19+
20+
OTEL_PYTHON_DISTRO = "OTEL_PYTHON_DISTRO"
21+
"""
22+
.. envvar:: OTEL_PYTHON_DISTRO
23+
"""
24+
25+
OTEL_PYTHON_CONFIGURATOR = "OTEL_PYTHON_CONFIGURATOR"
26+
"""
27+
.. envvar:: OTEL_PYTHON_CONFIGURATOR
28+
"""

0 commit comments

Comments
 (0)