Load instrumentors via Distro (#480)

This commit makes the following changes:

- Introduces a new `load_instrumentor(EntryPoint) -> None:` with a
default implementation method to the `BaseDistro` class.
- The default implementation loads the insrumentor from the provided
entry point and calls applies it without any arguments. (same as before)
- sitecustomize now calls Distro's `load_instrumentor` method to load
and activate an instrumentor instead of doing it directly.
- Added a new `DefaultDistro` implementation which is used if not distro
is found by entry points.
This commit is contained in:
Owais Lone
2021-05-05 00:23:08 +05:30
committed by GitHub
parent 01db88bfe8
commit 9199e3cb3e
4 changed files with 108 additions and 13 deletions

View File

@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#472](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/472))
- Set the `traced_request_attrs` of FalconInstrumentor by an argument correctly.
([#473](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/473))
- Distros can now implement `load_instrumentor(EntryPoint)` method to customize instrumentor
loading behaviour.
([#480](https://github.com/open-telemetry/opentelemetry-python/pull/480))
### Added
- Move `opentelemetry-instrumentation` from core repository

View File

@ -23,23 +23,34 @@ from pkg_resources import iter_entry_points
from opentelemetry.environment_variables import (
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
)
from opentelemetry.instrumentation.distro import BaseDistro, DefaultDistro
logger = getLogger(__file__)
def _load_distros():
def _load_distros() -> BaseDistro:
for entry_point in iter_entry_points("opentelemetry_distro"):
try:
entry_point.load()().configure() # type: ignore
logger.debug("Distribution %s configured", entry_point.name)
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():
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(",")
@ -47,13 +58,14 @@ def _load_instrumentors():
package_to_exclude = [x.strip() for x in package_to_exclude]
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:
if entry_point.name in package_to_exclude:
logger.debug(
"Instrumentation skipped for library %s", entry_point.name
)
continue
entry_point.load()().instrument() # type: ignore
distro.load_instrumentor(entry_point)
logger.debug("Instrumented %s", entry_point.name)
except Exception as exc: # pylint: disable=broad-except
logger.exception("Instrumenting of %s failed", entry_point.name)
@ -80,9 +92,10 @@ def _load_configurators():
def initialize():
try:
_load_distros()
distro = _load_distros()
distro.configure()
_load_configurators()
_load_instrumentors()
_load_instrumentors(distro)
except Exception: # pylint: disable=broad-except
logger.exception("Failed to auto initialize opentelemetry")
finally:

View File

@ -20,6 +20,10 @@ OpenTelemetry Base Distribution (Distro)
from abc import ABC, abstractmethod
from logging import getLogger
from pkg_resources import EntryPoint
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
_LOG = getLogger(__name__)
@ -43,5 +47,25 @@ class BaseDistro(ABC):
"""Configure the distribution"""
self._configure(**kwargs)
def load_instrumentor( # pylint: disable=no-self-use
self, entry_point: EntryPoint
):
"""Takes a collection of instrumentation entry points
and activates them by instantiating and calling instrument()
on each one.
__all__ = ["BaseDistro"]
Distros can override this method to customize the behavior by
inspecting each entry point and configuring them in special ways,
passing additional arguments, load a replacement/fork instead,
skip loading entirely, etc.
"""
instrumentor: BaseInstrumentor = entry_point.load()
instrumentor().instrument()
class DefaultDistro(BaseDistro):
def _configure(self, **kwargs):
pass
__all__ = ["BaseDistro", "DefaultDistro"]

View File

@ -0,0 +1,55 @@
# 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.
# type: ignore
from unittest import TestCase
from pkg_resources import EntryPoint
from opentelemetry.instrumentation.distro import BaseDistro
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
class MockInstrumetor(BaseInstrumentor):
def _instrument(self, **kwargs):
pass
def _uninstrument(self, **kwargs):
pass
class MockEntryPoint(EntryPoint):
def __init__(self, obj): # pylint: disable=super-init-not-called
self._obj = obj
def load(self, *args, **kwargs): # pylint: disable=signature-differs
return self._obj
class MockDistro(BaseDistro):
def _configure(self, **kwargs):
pass
class TestDistro(TestCase):
def test_load_instrumentor(self):
# pylint: disable=protected-access
distro = MockDistro()
instrumentor = MockInstrumetor()
entry_point = MockEntryPoint(MockInstrumetor)
self.assertFalse(instrumentor._is_instrumented)
distro.load_instrumentor(entry_point)
self.assertTrue(instrumentor._is_instrumented)