Skip to content

Commit

Permalink
Load instrumentors via Distro
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
owais committed Apr 28, 2021
1 parent 3ec7736 commit b11ed56
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- `opentelemetry-propagator-ot-trace` Use `TraceFlags` object in `extract`
([#472](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/472))
- 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,49 @@
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(",")
# 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_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)
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@
from abc import ABC, abstractmethod
from logging import getLogger

from pkg_resources import EntryPoint

from opentelemetry.instrumentation.instrumentor import BaseInstrumentor

_LOG = getLogger(__name__)


Expand All @@ -43,5 +47,25 @@ def configure(self, **kwargs):
"""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.
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"]
__all__ = ["BaseDistro", "DefaultDistro"]
55 changes: 55 additions & 0 deletions opentelemetry-instrumentation/tests/test_distro.py
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit b11ed56

Please sign in to comment.