Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Distro selection #1823

Merged
merged 1 commit into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#1810](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1810))
- `opentelemetry-instrumentation-urllib3` Add support for urllib3 version 2
([#1879](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1879))
- Add optional distro and configurator selection for auto-instrumentation
([#1823](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1823))

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

- `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))
- Add request and response hooks for GRPC instrumentation (client only)
([#1706](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1706))
- Fix memory leak in SQLAlchemy instrumentation where disposed `Engine` does not get garbage collected
([#1771](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1771)
([#1771](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1771))
- `opentelemetry-instrumentation-pymemcache` Update instrumentation to support pymemcache >4
([#1764](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1764))
- `opentelemetry-instrumentation-confluent-kafka` Add support for higher versions of confluent_kafka
Expand Down Expand Up @@ -86,7 +88,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

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

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

Expand Down
7 changes: 6 additions & 1 deletion opentelemetry-instrumentation/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ This package provides a couple of commands that help automatically instruments a

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

pip install opentelemetry-distro[otlp]

When creating a custom distro and/or configurator, be sure to add entry points for each under `opentelemetry_distro` and `opentelemetry_configurator` respectfully.
jeremydvoss marked this conversation as resolved.
Show resolved Hide resolved
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.


opentelemetry-bootstrap
-----------------------
Expand Down Expand Up @@ -58,6 +61,8 @@ The command supports the following configuration options as CLI arguments and en

* ``--traces_exporter`` or ``OTEL_TRACES_EXPORTER``
* ``--metrics_exporter`` or ``OTEL_METRICS_EXPORTER``
* ``--distro`` or ``OTEL_PYTHON_DISTRO``
* ``--configurator`` or ``OTEL_PYTHON_CONFIGURATOR``

Used to specify which trace exporter to use. Can be set to one or more of the well-known exporter
names (see below).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# Copyright The OpenTelemetry Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from logging import getLogger
from os import environ

from pkg_resources import iter_entry_points

from opentelemetry.instrumentation.dependencies import (
get_dist_dependency_conflicts,
)
from opentelemetry.instrumentation.distro import BaseDistro, DefaultDistro
from opentelemetry.instrumentation.environment_variables import (
OTEL_PYTHON_CONFIGURATOR,
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
OTEL_PYTHON_DISTRO,
)
from opentelemetry.instrumentation.version import __version__

_logger = getLogger(__name__)


def _load_distro() -> BaseDistro:
distro_name = environ.get(OTEL_PYTHON_DISTRO, None)
jeremydvoss marked this conversation as resolved.
Show resolved Hide resolved
for entry_point in iter_entry_points("opentelemetry_distro"):
try:
# If no distro is specified, use first to come up.
if distro_name is None or distro_name == entry_point.name:
jeremydvoss marked this conversation as resolved.
Show resolved Hide resolved
distro = entry_point.load()()
if not isinstance(distro, BaseDistro):
_logger.debug(
"%s is not an OpenTelemetry Distro. Skipping",
entry_point.name,
)
continue
_logger.debug(
"Distribution %s will be configured", entry_point.name
)
return distro
except Exception as exc: # pylint: disable=broad-except
_logger.exception(
"Distribution %s configuration failed", entry_point.name
)
raise exc
return DefaultDistro()


def _load_instrumentors(distro):
package_to_exclude = environ.get(OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, [])
if isinstance(package_to_exclude, str):
package_to_exclude = package_to_exclude.split(",")
# to handle users entering "requests , flask" or "requests, flask" with spaces
package_to_exclude = [x.strip() for x in package_to_exclude]

for entry_point in iter_entry_points("opentelemetry_pre_instrument"):
entry_point.load()()

for entry_point in iter_entry_points("opentelemetry_instrumentor"):
if entry_point.name in package_to_exclude:
_logger.debug(
"Instrumentation skipped for library %s", entry_point.name
)
continue

try:
conflict = get_dist_dependency_conflicts(entry_point.dist)
if conflict:
_logger.debug(
"Skipping instrumentation %s: %s",
entry_point.name,
conflict,
)
continue

# tell instrumentation to not run dep checks again as we already did it above
distro.load_instrumentor(entry_point, skip_dep_check=True)
_logger.debug("Instrumented %s", entry_point.name)
except Exception as exc: # pylint: disable=broad-except
_logger.exception("Instrumenting of %s failed", entry_point.name)
raise exc

for entry_point in iter_entry_points("opentelemetry_post_instrument"):
entry_point.load()()


def _load_configurators():
configurator_name = environ.get(OTEL_PYTHON_CONFIGURATOR, None)
configured = None
for entry_point in iter_entry_points("opentelemetry_configurator"):
if configured is not None:
_logger.warning(
"Configuration of %s not loaded, %s already loaded",
entry_point.name,
configured,
)
continue
try:
if (
configurator_name is None
or configurator_name == entry_point.name
):
entry_point.load()().configure(auto_instrumentation_version=__version__) # type: ignore
configured = entry_point.name
else:
_logger.warning(
"Configuration of %s not loaded because %s is set by %s",
entry_point.name,
configurator_name,
OTEL_PYTHON_CONFIGURATOR,
)
except Exception as exc: # pylint: disable=broad-except
_logger.exception("Configuration of %s failed", entry_point.name)
raise exc
Original file line number Diff line number Diff line change
Expand Up @@ -16,107 +16,24 @@
from os import environ
from os.path import abspath, dirname, pathsep

from pkg_resources import iter_entry_points

from opentelemetry.instrumentation.dependencies import (
get_dist_dependency_conflicts,
)
from opentelemetry.instrumentation.distro import BaseDistro, DefaultDistro
from opentelemetry.instrumentation.environment_variables import (
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
from opentelemetry.instrumentation.auto_instrumentation._load import (
_load_configurators,
_load_distro,
_load_instrumentors,
)
from opentelemetry.instrumentation.utils import _python_path_without_directory
from opentelemetry.instrumentation.version import __version__

logger = getLogger(__name__)


def _load_distros() -> BaseDistro:
for entry_point in iter_entry_points("opentelemetry_distro"):
try:
distro = entry_point.load()()
if not isinstance(distro, BaseDistro):
logger.debug(
"%s is not an OpenTelemetry Distro. Skipping",
entry_point.name,
)
continue
logger.debug(
"Distribution %s will be configured", entry_point.name
)
return distro
except Exception as exc: # pylint: disable=broad-except
logger.exception(
"Distribution %s configuration failed", entry_point.name
)
raise exc
return DefaultDistro()


def _load_instrumentors(distro):
package_to_exclude = environ.get(OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, [])
if isinstance(package_to_exclude, str):
package_to_exclude = package_to_exclude.split(",")
# to handle users entering "requests , flask" or "requests, flask" with spaces
package_to_exclude = [x.strip() for x in package_to_exclude]

for entry_point in iter_entry_points("opentelemetry_pre_instrument"):
entry_point.load()()

for entry_point in iter_entry_points("opentelemetry_instrumentor"):
if entry_point.name in package_to_exclude:
logger.debug(
"Instrumentation skipped for library %s", entry_point.name
)
continue

try:
conflict = get_dist_dependency_conflicts(entry_point.dist)
if conflict:
logger.debug(
"Skipping instrumentation %s: %s",
entry_point.name,
conflict,
)
continue

# tell instrumentation to not run dep checks again as we already did it above
distro.load_instrumentor(entry_point, skip_dep_check=True)
logger.debug("Instrumented %s", entry_point.name)
except Exception as exc: # pylint: disable=broad-except
logger.exception("Instrumenting of %s failed", entry_point.name)
raise exc

for entry_point in iter_entry_points("opentelemetry_post_instrument"):
entry_point.load()()


def _load_configurators():
configured = None
for entry_point in iter_entry_points("opentelemetry_configurator"):
if configured is not None:
logger.warning(
"Configuration of %s not loaded, %s already loaded",
entry_point.name,
configured,
)
continue
try:
entry_point.load()().configure(auto_instrumentation_version=__version__) # type: ignore
configured = entry_point.name
except Exception as exc: # pylint: disable=broad-except
logger.exception("Configuration of %s failed", entry_point.name)
raise exc


def initialize():
# prevents auto-instrumentation of subprocesses if code execs another python process
environ["PYTHONPATH"] = _python_path_without_directory(
environ["PYTHONPATH"], dirname(abspath(__file__)), pathsep
)

try:
distro = _load_distros()
distro = _load_distro()
distro.configure()
_load_configurators()
_load_instrumentors(distro)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,13 @@
"""
.. envvar:: OTEL_PYTHON_DISABLED_INSTRUMENTATIONS
"""

OTEL_PYTHON_DISTRO = "OTEL_PYTHON_DISTRO"
jeremydvoss marked this conversation as resolved.
Show resolved Hide resolved
"""
.. envvar:: OTEL_PYTHON_DISTRO
"""

OTEL_PYTHON_CONFIGURATOR = "OTEL_PYTHON_CONFIGURATOR"
jeremydvoss marked this conversation as resolved.
Show resolved Hide resolved
"""
.. envvar:: OTEL_PYTHON_CONFIGURATOR
"""
Loading