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

Default configurators do more for distros #1937

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 @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.4.0-0.23b0...HEAD)
- `opentelemetry-distro` & `opentelemetry-sdk` Moved Auto Instrumentation Configurator code to SDK
to let distros use its default implementation
([#1937](https://github.com/open-telemetry/opentelemetry-python/pull/1937))

## [1.4.0-0.23b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.4.0-0.23b0) - 2021-07-21

Expand Down
2 changes: 1 addition & 1 deletion opentelemetry-distro/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ where = src
opentelemetry_distro =
distro = opentelemetry.distro:OpenTelemetryDistro
opentelemetry_configurator =
configurator = opentelemetry.distro:Configurator
configurator = opentelemetry.distro:OpenTelemetryConfigurator

[options.extras_require]
test =
Expand Down
138 changes: 4 additions & 134 deletions opentelemetry-distro/src/opentelemetry/distro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,144 +13,14 @@
# limitations under the License.
#
import os
from logging import getLogger
from os import environ
from typing import Sequence, Tuple

from pkg_resources import iter_entry_points

from opentelemetry import trace
from opentelemetry.environment_variables import (
OTEL_PYTHON_ID_GENERATOR,
OTEL_TRACES_EXPORTER,
)
from opentelemetry.instrumentation.configurator import BaseConfigurator
from opentelemetry.environment_variables import OTEL_TRACES_EXPORTER
from opentelemetry.instrumentation.distro import BaseDistro
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
from opentelemetry.sdk.trace.id_generator import IdGenerator

logger = getLogger(__file__)


EXPORTER_OTLP = "otlp"
EXPORTER_OTLP_SPAN = "otlp_proto_grpc_span"

RANDOM_ID_GENERATOR = "random"
_DEFAULT_ID_GENERATOR = RANDOM_ID_GENERATOR


def _get_id_generator() -> str:
return environ.get(OTEL_PYTHON_ID_GENERATOR, _DEFAULT_ID_GENERATOR)


def _get_exporter_names() -> Sequence[str]:
trace_exporters = environ.get(OTEL_TRACES_EXPORTER)

exporters = set()

if trace_exporters and trace_exporters.lower().strip() != "none":
exporters.update(
{
trace_exporter.strip()
for trace_exporter in trace_exporters.split(",")
}
)

if EXPORTER_OTLP in exporters:
exporters.remove(EXPORTER_OTLP)
exporters.add(EXPORTER_OTLP_SPAN)

return list(exporters)


def _init_tracing(
exporters: Sequence[SpanExporter], id_generator: IdGenerator
):
# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
# from the env variable else defaults to "unknown_service"
provider = TracerProvider(
id_generator=id_generator(),
)
trace.set_tracer_provider(provider)

for _, exporter_class in exporters.items():
exporter_args = {}
provider.add_span_processor(
BatchSpanProcessor(exporter_class(**exporter_args))
)
from opentelemetry.sdk._configuration import _OTelSDKConfigurator


def _import_tracer_provider_config_components(
selected_components, entry_point_name
) -> Sequence[Tuple[str, object]]:
component_entry_points = {
ep.name: ep for ep in iter_entry_points(entry_point_name)
}
component_impls = []
for selected_component in selected_components:
entry_point = component_entry_points.get(selected_component, None)
if not entry_point:
raise RuntimeError(
"Requested component '{}' not found in entry points for '{}'".format(
selected_component, entry_point_name
)
)

component_impl = entry_point.load()
component_impls.append((selected_component, component_impl))

return component_impls


def _import_exporters(
exporter_names: Sequence[str],
) -> Sequence[SpanExporter]:
trace_exporters = {}

for (
exporter_name,
exporter_impl,
) in _import_tracer_provider_config_components(
exporter_names, "opentelemetry_exporter"
):
if issubclass(exporter_impl, SpanExporter):
trace_exporters[exporter_name] = exporter_impl
else:
raise RuntimeError(
"{0} is not a trace exporter".format(exporter_name)
)
return trace_exporters


def _import_id_generator(id_generator_name: str) -> IdGenerator:
# pylint: disable=unbalanced-tuple-unpacking
[
(id_generator_name, id_generator_impl)
] = _import_tracer_provider_config_components(
[id_generator_name.strip()], "opentelemetry_id_generator"
)

if issubclass(id_generator_impl, IdGenerator):
return id_generator_impl

raise RuntimeError("{0} is not an IdGenerator".format(id_generator_name))


def _initialize_components():
exporter_names = _get_exporter_names()
trace_exporters = _import_exporters(exporter_names)
id_generator_name = _get_id_generator()
id_generator = _import_id_generator(id_generator_name)
_init_tracing(trace_exporters, id_generator)


class Configurator(BaseConfigurator):

# pylint: disable=no-self-use
def _configure(self, **kwargs):
_initialize_components()
class OpenTelemetryConfigurator(_OTelSDKConfigurator):
NathanielRN marked this conversation as resolved.
Show resolved Hide resolved
pass


class OpenTelemetryDistro(BaseDistro):
Expand Down
160 changes: 160 additions & 0 deletions opentelemetry-sdk/src/opentelemetry/sdk/_configuration/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# 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.
#

"""
OpenTelemetry SDK Configurator for Easy Instrumentation with Distros
"""

from os import environ
from typing import Sequence, Tuple

from pkg_resources import iter_entry_points

from opentelemetry import trace
from opentelemetry.environment_variables import (
OTEL_PYTHON_ID_GENERATOR,
OTEL_TRACES_EXPORTER,
)
from opentelemetry.instrumentation.configurator import BaseConfigurator
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
from opentelemetry.sdk.trace.id_generator import IdGenerator

_EXPORTER_OTLP = "otlp"
_EXPORTER_OTLP_SPAN = "otlp_proto_grpc_span"

_RANDOM_ID_GENERATOR = "random"
_DEFAULT_ID_GENERATOR = _RANDOM_ID_GENERATOR


def _get_id_generator() -> str:
return environ.get(OTEL_PYTHON_ID_GENERATOR, _DEFAULT_ID_GENERATOR)


def _get_exporter_names() -> Sequence[str]:
trace_exporters = environ.get(OTEL_TRACES_EXPORTER)

exporters = set()

if trace_exporters and trace_exporters.lower().strip() != "none":
exporters.update(
{
trace_exporter.strip()
for trace_exporter in trace_exporters.split(",")
}
)

if _EXPORTER_OTLP in exporters:
exporters.remove(_EXPORTER_OTLP)
exporters.add(_EXPORTER_OTLP_SPAN)

return list(exporters)


def _init_tracing(
exporters: Sequence[SpanExporter], id_generator: IdGenerator
):
# if env var OTEL_RESOURCE_ATTRIBUTES is given, it will read the service_name
# from the env variable else defaults to "unknown_service"
provider = TracerProvider(
id_generator=id_generator(),
)
trace.set_tracer_provider(provider)

for _, exporter_class in exporters.items():
exporter_args = {}
provider.add_span_processor(
BatchSpanProcessor(exporter_class(**exporter_args))
)


def _import_tracer_provider_config_components(
selected_components, entry_point_name
) -> Sequence[Tuple[str, object]]:
component_entry_points = {
ep.name: ep for ep in iter_entry_points(entry_point_name)
}
component_impls = []
for selected_component in selected_components:
entry_point = component_entry_points.get(selected_component, None)
if not entry_point:
raise RuntimeError(
"Requested component '{}' not found in entry points for '{}'".format(
selected_component, entry_point_name
)
)

component_impl = entry_point.load()
component_impls.append((selected_component, component_impl))

return component_impls


def _import_exporters(
exporter_names: Sequence[str],
) -> Sequence[SpanExporter]:
trace_exporters = {}

for (
exporter_name,
exporter_impl,
) in _import_tracer_provider_config_components(
exporter_names, "opentelemetry_exporter"
):
if issubclass(exporter_impl, SpanExporter):
trace_exporters[exporter_name] = exporter_impl
else:
raise RuntimeError(
"{0} is not a trace exporter".format(exporter_name)
)
return trace_exporters


def _import_id_generator(id_generator_name: str) -> IdGenerator:
# pylint: disable=unbalanced-tuple-unpacking
[
(id_generator_name, id_generator_impl)
] = _import_tracer_provider_config_components(
[id_generator_name.strip()], "opentelemetry_id_generator"
)

if issubclass(id_generator_impl, IdGenerator):
return id_generator_impl

raise RuntimeError("{0} is not an IdGenerator".format(id_generator_name))


def _initialize_components():
exporter_names = _get_exporter_names()
trace_exporters = _import_exporters(exporter_names)
id_generator_name = _get_id_generator()
id_generator = _import_id_generator(id_generator_name)
_init_tracing(trace_exporters, id_generator)


class _OTelSDKConfigurator(BaseConfigurator):
"""A basic Configurator by OTel Python for initalizing OTel SDK components

Initializes several crucial OTel SDK components (i.e. TracerProvider,
MeterProvider, Processors...) according to a default implementation. Other
Configurators can subclass and slightly alter this initialization.

NOTE: This class should not be instantiated nor should it become an entry
point on the `opentelemetry-sdk` package. Instead, distros should subclass
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should distros be subclassing this class or OpenTelemetryConfigurator?

this Configurator and enchance it as needed.
"""

def _configure(self, **kwargs):
_initialize_components()
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
from unittest.mock import patch

from opentelemetry import trace
from opentelemetry.distro import (
EXPORTER_OTLP,
EXPORTER_OTLP_SPAN,
from opentelemetry.environment_variables import (
OTEL_PYTHON_ID_GENERATOR,
OTEL_TRACES_EXPORTER,
)
from opentelemetry.sdk._configuration import (
_EXPORTER_OTLP,
_EXPORTER_OTLP_SPAN,
_get_exporter_names,
_get_id_generator,
_import_id_generator,
_init_tracing,
)
from opentelemetry.environment_variables import (
OTEL_PYTHON_ID_GENERATOR,
OTEL_TRACES_EXPORTER,
)
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
from opentelemetry.sdk.trace.id_generator import IdGenerator, RandomIdGenerator

Expand Down Expand Up @@ -87,10 +87,10 @@ class TestTraceInit(TestCase):
def setUp(self):
super()
self.get_provider_patcher = patch(
"opentelemetry.distro.TracerProvider", Provider
"opentelemetry.sdk._configuration.TracerProvider", Provider
)
self.get_processor_patcher = patch(
"opentelemetry.distro.BatchSpanProcessor", Processor
"opentelemetry.sdk._configuration.BatchSpanProcessor", Processor
)
self.set_provider_patcher = patch(
"opentelemetry.trace.set_tracer_provider"
Expand Down Expand Up @@ -143,8 +143,8 @@ def test_trace_init_otlp(self):
)

@patch.dict(environ, {OTEL_PYTHON_ID_GENERATOR: "custom_id_generator"})
@patch("opentelemetry.distro.IdGenerator", new=IdGenerator)
@patch("opentelemetry.distro.iter_entry_points")
@patch("opentelemetry.sdk._configuration.IdGenerator", new=IdGenerator)
@patch("opentelemetry.sdk._configuration.iter_entry_points")
def test_trace_init_custom_id_generator(self, mock_iter_entry_points):
mock_iter_entry_points.configure_mock(
return_value=[
Expand All @@ -160,9 +160,9 @@ def test_trace_init_custom_id_generator(self, mock_iter_entry_points):

class TestExporterNames(TestCase):
def test_otlp_exporter_overwrite(self):
for exporter in [EXPORTER_OTLP, EXPORTER_OTLP_SPAN]:
for exporter in [_EXPORTER_OTLP, _EXPORTER_OTLP_SPAN]:
with patch.dict(environ, {OTEL_TRACES_EXPORTER: exporter}):
self.assertEqual(_get_exporter_names(), [EXPORTER_OTLP_SPAN])
self.assertEqual(_get_exporter_names(), [_EXPORTER_OTLP_SPAN])

@patch.dict(environ, {OTEL_TRACES_EXPORTER: "jaeger,zipkin"})
def test_multiple_exporters(self):
Expand Down