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

Load instrumentors via Distro #480

Merged
merged 2 commits into from
May 4, 2021
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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)