From 0283fb2e185ebe7a6a4ce771a5aef2d1d3e6db83 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Mon, 14 Dec 2020 17:04:47 -0800 Subject: [PATCH 01/12] adding opentelemetry-distro --- opentelemetry-distro/MANIFEST.in | 7 ++ opentelemetry-distro/README.rst | 111 ++++++++++++++++++ opentelemetry-distro/setup.cfg | 56 +++++++++ opentelemetry-distro/setup.py | 27 +++++ .../src/opentelemetry/distro/__init__.py | 28 +++++ .../src/opentelemetry/distro/version.py | 15 +++ opentelemetry-distro/tests/__init__.py | 0 opentelemetry-distro/tests/test_distro.py | 27 +++++ .../opentelemetry/instrumentation/distro.py | 47 ++++++++ 9 files changed, 318 insertions(+) create mode 100644 opentelemetry-distro/MANIFEST.in create mode 100644 opentelemetry-distro/README.rst create mode 100644 opentelemetry-distro/setup.cfg create mode 100644 opentelemetry-distro/setup.py create mode 100644 opentelemetry-distro/src/opentelemetry/distro/__init__.py create mode 100644 opentelemetry-distro/src/opentelemetry/distro/version.py create mode 100644 opentelemetry-distro/tests/__init__.py create mode 100644 opentelemetry-distro/tests/test_distro.py create mode 100644 opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py diff --git a/opentelemetry-distro/MANIFEST.in b/opentelemetry-distro/MANIFEST.in new file mode 100644 index 0000000000..191b7d1959 --- /dev/null +++ b/opentelemetry-distro/MANIFEST.in @@ -0,0 +1,7 @@ +prune tests +graft src +global-exclude *.pyc +global-exclude *.pyo +global-exclude __pycache__/* +include MANIFEST.in +include README.rst diff --git a/opentelemetry-distro/README.rst b/opentelemetry-distro/README.rst new file mode 100644 index 0000000000..027ab99f0a --- /dev/null +++ b/opentelemetry-distro/README.rst @@ -0,0 +1,111 @@ +OpenTelemetry Distro +==================== + +|pypi| + +.. |pypi| image:: https://badge.fury.io/py/opentelemetry-distro.svg + :target: https://pypi.org/project/opentelemetry-distro/ + +Installation +------------ + +:: + + pip install opentelemetry-distro + + +This package provides a couple of commands that help automatically instruments a program: + + +opentelemetry-bootstrap +----------------------- + +:: + + opentelemetry-bootstrap --action=install|requirements + +This commands inspects the active Python site-packages and figures out which +instrumentation packages the user might want to install. By default it prints out +a list of the suggested instrumentation packages which can be added to a requirements.txt +file. It also supports installing the suggested packages when run with :code:`--action=install` +flag. + + +opentelemetry-instrument +------------------------ + +:: + + opentelemetry-instrument python program.py + +The instrument command will try to automatically detect packages used by your python program +and when possible, apply automatic tracing instrumentation on them. This means your program +will get automatic distributed tracing for free without having to make any code changes +at all. This will also configure a global tracer and tracing exporter without you having to +make any code changes. By default, the instrument command will use the OTLP exporter but +this can be overriden when needed. + +The command supports the following configuration options as CLI arguments and environment vars: + + +* ``--exporter`` or ``OTEL_EXPORTER`` + +Used to specify which trace exporter to use. Can be set to one or more +of the well-known exporter names (see below). + + - Defaults to `otlp`. + - Can be set to `none` to disable automatic tracer initialization. + +You can pass multiple values to configure multiple exporters e.g, ``zipkin,prometheus`` + +Well known trace exporter names: + + - jaeger + - opencensus + - otlp + - otlp_span + - otlp_metric + - zipkin + +``otlp`` is an alias for ``otlp_span,otlp_metric``. + +* ``--service-name`` or ``OTEL_SERVICE_NAME`` + +When present the value is passed on to the relevant exporter initializer as ``service_name`` argument. + +* ``--ids-generator`` or ``OTEL_IDS_GENERATOR`` + +Used to specify which IDs Generator to use for the global Tracer Provider. By default, it +will use the random IDs generator. + +The code in ``program.py`` needs to use one of the packages for which there is +an OpenTelemetry integration. For a list of the available integrations please +check `here `_ + +Examples +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + opentelemetry-instrument -e otlp flask run --port=3000 + +The above command will pass ``-e otlp`` to the instrument command and ``--port=3000`` to ``flask run``. + +:: + + opentelemetry-instrument -e zipkin,otlp celery -A tasks worker --loglevel=info + +The above command will configure global trace provider, attach zipkin and otlp exporters to it and then +start celery with the rest of the arguments. + +:: + + opentelemetry-instrument --ids-generator random flask run --port=3000 + +The above command will configure the global trace provider to use the Random IDs Generator, and then +pass ``--port=3000`` to ``flask run``. + +References +---------- + +* `OpenTelemetry Project `_ diff --git a/opentelemetry-distro/setup.cfg b/opentelemetry-distro/setup.cfg new file mode 100644 index 0000000000..f7fd1b5bd3 --- /dev/null +++ b/opentelemetry-distro/setup.cfg @@ -0,0 +1,56 @@ +# 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. +# +[metadata] +name = opentelemetry-distro +description = OpenTelemetry Python Distro +long_description = file: README.rst +long_description_content_type = text/x-rst +author = OpenTelemetry Authors +author_email = cncf-opentelemetry-contributors@lists.cncf.io +url = https://github.com/open-telemetry/opentelemetry-python/tree/master/opentelemetry-distro +platforms = any +license = Apache-2.0 +classifiers = + Development Status :: 4 - Beta + Intended Audience :: Developers + License :: OSI Approved :: Apache Software License + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + +[options] +python_requires = >=3.5 +package_dir= + =src +packages=find_namespace: +zip_safe = False +include_package_data = True +install_requires = + opentelemetry-api == 0.17.dev0 + opentelemetry-sdk == 0.17.dev0 + opentelemetry-exporter-otlp == 0.17.dev0 + +[options.packages.find] +where = src + +[options.entry_points] +opentelemetry_distro = + otel_distro = opentelemetry.distro:OpenTelemetryDistro + +[options.extras_require] +test = diff --git a/opentelemetry-distro/setup.py b/opentelemetry-distro/setup.py new file mode 100644 index 0000000000..da8417d50a --- /dev/null +++ b/opentelemetry-distro/setup.py @@ -0,0 +1,27 @@ +# 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. + +import os + +import setuptools + +BASE_DIR = os.path.dirname(__file__) +VERSION_FILENAME = os.path.join( + BASE_DIR, "src", "opentelemetry", "distro", "version.py" +) +PACKAGE_INFO = {} +with open(VERSION_FILENAME) as f: + exec(f.read(), PACKAGE_INFO) + +setuptools.setup(version=PACKAGE_INFO["__version__"],) diff --git a/opentelemetry-distro/src/opentelemetry/distro/__init__.py b/opentelemetry-distro/src/opentelemetry/distro/__init__.py new file mode 100644 index 0000000000..f5a450a1f8 --- /dev/null +++ b/opentelemetry-distro/src/opentelemetry/distro/__init__.py @@ -0,0 +1,28 @@ +# 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. +# +import os + +from opentelemetry.instrumentation.distro import BaseDistro + + +class OpenTelemetryDistro(BaseDistro): + """ + The OpenTelemetry provided Distro configures a default set of + configuration out of the box. + """ + + def _configure(self, **kwargs): + os.environ.setdefault("OTEL_EXPORTER", "otlp") + self._validate() diff --git a/opentelemetry-distro/src/opentelemetry/distro/version.py b/opentelemetry-distro/src/opentelemetry/distro/version.py new file mode 100644 index 0000000000..86e1dbb17a --- /dev/null +++ b/opentelemetry-distro/src/opentelemetry/distro/version.py @@ -0,0 +1,15 @@ +# 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. + +__version__ = "0.17.dev0" diff --git a/opentelemetry-distro/tests/__init__.py b/opentelemetry-distro/tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/opentelemetry-distro/tests/test_distro.py b/opentelemetry-distro/tests/test_distro.py new file mode 100644 index 0000000000..6c6a694161 --- /dev/null +++ b/opentelemetry-distro/tests/test_distro.py @@ -0,0 +1,27 @@ +# 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 DistributionNotFound, require + + +class TestDistribution(TestCase): + def test_proto(self): + + try: + require(["opentelemetry-distro"]) + except DistributionNotFound: + self.fail("opentelemetry-distro not installed") diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py new file mode 100644 index 0000000000..63cacf1f6d --- /dev/null +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/distro.py @@ -0,0 +1,47 @@ +# 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 + +""" +OpenTelemetry Base Distribution (Distro) +""" + +from abc import ABC, abstractmethod +from logging import getLogger + +_LOG = getLogger(__name__) + + +class BaseDistro(ABC): + """An ABC for distro""" + + _instance = None + + def __new__(cls, *args, **kwargs): + + if cls._instance is None: + cls._instance = object.__new__(cls, *args, **kwargs) + + return cls._instance + + @abstractmethod + def _configure(self, **kwargs): + """Configure the distribution""" + + def configure(self, **kwargs): + """Configure the distribution""" + self._configure(**kwargs) + + +__all__ = ["BaseDistro"] From 6447458ed8c4e4d04c9d2b69665fa94727929a99 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Mon, 14 Dec 2020 17:15:16 -0800 Subject: [PATCH 02/12] adding distro to various config --- eachdist.ini | 1 + scripts/build.sh | 2 +- tox.ini | 9 +++++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/eachdist.ini b/eachdist.ini index 9cef826d20..ded3a5d8c1 100644 --- a/eachdist.ini +++ b/eachdist.ini @@ -9,6 +9,7 @@ sortfirst= opentelemetry-sdk opentelemetry-instrumentation opentelemetry-proto + opentelemetry-distro tests/util instrumentation/* exporter/* diff --git a/scripts/build.sh b/scripts/build.sh index 0dec4a27ba..a061e998f8 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -16,7 +16,7 @@ DISTDIR=dist mkdir -p $DISTDIR rm -rf $DISTDIR/* - for d in opentelemetry-api/ opentelemetry-sdk/ opentelemetry-instrumentation/ opentelemetry-proto/ exporter/*/ instrumentation/*/ ; do + for d in opentelemetry-api/ opentelemetry-sdk/ opentelemetry-instrumentation/ opentelemetry-proto/ opentelemetry-distro/ exporter/*/ instrumentation/*/ ; do ( echo "building $d" cd "$d" diff --git a/tox.ini b/tox.ini index 9a7c8ce3f8..04d5552064 100644 --- a/tox.ini +++ b/tox.ini @@ -24,6 +24,10 @@ envlist = py3{5,6,7,8,9}-test-core-getting-started pypy3-test-core-getting-started + ; opentelemetry-distro + py3{5,6,7,8,9}-test-core-distro + pypy3-test-core-distro + ; opentelemetry-exporter-jaeger py3{5,6,7,8,9}-test-exporter-jaeger pypy3-test-exporter-jaeger @@ -71,6 +75,7 @@ changedir = test-core-proto: opentelemetry-proto/tests test-core-instrumentation: opentelemetry-instrumentation/tests test-core-getting-started: docs/getting_started/tests + test-core-distro: opentelemetry-distro/tests test-core-opentracing-shim: instrumentation/opentelemetry-instrumentation-opentracing-shim/tests test-exporter-jaeger: exporter/opentelemetry-exporter-jaeger/tests @@ -87,6 +92,9 @@ commands_pre = test: pip install {toxinidir}/opentelemetry-api {toxinidir}/opentelemetry-instrumentation {toxinidir}/opentelemetry-sdk {toxinidir}/tests/util test-core-proto: pip install {toxinidir}/opentelemetry-proto + distro: pip install {toxinidir}/opentelemetry-proto + distro: pip install {toxinidir}/exporter/opentelemetry-exporter-otlp + distro: pip install {toxinidir}/opentelemetry-distro {toxinidir}/opentelemetry-distro instrumentation: pip install {toxinidir}/opentelemetry-instrumentation getting-started: pip install -e {toxinidir}/opentelemetry-instrumentation -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-requests -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-wsgi -e {toxinidir}/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-flask @@ -151,6 +159,7 @@ commands_pre = python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-otlp[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-prometheus[test] python -m pip install -e {toxinidir}/exporter/opentelemetry-exporter-zipkin[test] + python -m pip install -e {toxinidir}/opentelemetry-distro[test] commands = python scripts/eachdist.py lint --check-only From 2e7b780cfc48fcedaf2d4b7e4c3fd6fee5729777 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Mon, 14 Dec 2020 17:18:42 -0800 Subject: [PATCH 03/12] update default exporter in SDK to none --- .../src/opentelemetry/distro/__init__.py | 1 - .../auto_instrumentation/sitecustomize.py | 11 +++++++++-- .../src/opentelemetry/sdk/configuration/__init__.py | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/opentelemetry-distro/src/opentelemetry/distro/__init__.py b/opentelemetry-distro/src/opentelemetry/distro/__init__.py index f5a450a1f8..083881387e 100644 --- a/opentelemetry-distro/src/opentelemetry/distro/__init__.py +++ b/opentelemetry-distro/src/opentelemetry/distro/__init__.py @@ -25,4 +25,3 @@ class OpenTelemetryDistro(BaseDistro): def _configure(self, **kwargs): os.environ.setdefault("OTEL_EXPORTER", "otlp") - self._validate() diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py index 2a51d0b803..348175cfbe 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py @@ -22,8 +22,15 @@ def _load_distros(): - # will be implemented in a subsequent PR - pass + for entry_point in iter_entry_points("opentelemetry_distro"): + try: + entry_point.load()().configure() # type: ignore + logger.debug("Distribution %s configured", entry_point.name) + except Exception as exc: # pylint: disable=broad-except + logger.exception( + "Distribution %s configuration failed", entry_point.name + ) + raise exc def _load_instrumentors(): diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py index 477aa5cf62..fdd25933d8 100644 --- a/opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py +++ b/opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py @@ -33,7 +33,7 @@ EXPORTER_OTLP = "otlp" EXPORTER_OTLP_SPAN = "otlp_span" EXPORTER_OTLP_METRIC = "otlp_metric" -_DEFAULT_EXPORTER = EXPORTER_OTLP +_DEFAULT_EXPORTER = "none" RANDOM_IDS_GENERATOR = "random" _DEFAULT_IDS_GENERATOR = RANDOM_IDS_GENERATOR From a1fe106ac35661f9610b2e2975dfcbef039cde55 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Mon, 14 Dec 2020 17:24:09 -0800 Subject: [PATCH 04/12] update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 078f02791f..59fb81aa77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `opentelemetry-sdk` package now registers an entrypoint `opentelemetry_configurator` to allow `opentelemetry-instrument` to load the configuration for the SDK ([#1420](https://github.com/open-telemetry/opentelemetry-python/pull/1420)) +- Adding `opentelemetry-distro` package to add default configuration for + span exporter to OTLP + ([#1482](https://github.com/open-telemetry/opentelemetry-python/pull/1482)) ## [0.16b1](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v0.16b1) - 2020-11-26 ### Added From c2031c547be28856a90e85b7a1fe34da701f4681 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Tue, 12 Jan 2021 14:25:25 -0800 Subject: [PATCH 05/12] cleanup readme --- opentelemetry-distro/README.rst | 91 +-------------------------------- 1 file changed, 1 insertion(+), 90 deletions(-) diff --git a/opentelemetry-distro/README.rst b/opentelemetry-distro/README.rst index 027ab99f0a..4189131fc2 100644 --- a/opentelemetry-distro/README.rst +++ b/opentelemetry-distro/README.rst @@ -14,96 +14,7 @@ Installation pip install opentelemetry-distro -This package provides a couple of commands that help automatically instruments a program: - - -opentelemetry-bootstrap ------------------------ - -:: - - opentelemetry-bootstrap --action=install|requirements - -This commands inspects the active Python site-packages and figures out which -instrumentation packages the user might want to install. By default it prints out -a list of the suggested instrumentation packages which can be added to a requirements.txt -file. It also supports installing the suggested packages when run with :code:`--action=install` -flag. - - -opentelemetry-instrument ------------------------- - -:: - - opentelemetry-instrument python program.py - -The instrument command will try to automatically detect packages used by your python program -and when possible, apply automatic tracing instrumentation on them. This means your program -will get automatic distributed tracing for free without having to make any code changes -at all. This will also configure a global tracer and tracing exporter without you having to -make any code changes. By default, the instrument command will use the OTLP exporter but -this can be overriden when needed. - -The command supports the following configuration options as CLI arguments and environment vars: - - -* ``--exporter`` or ``OTEL_EXPORTER`` - -Used to specify which trace exporter to use. Can be set to one or more -of the well-known exporter names (see below). - - - Defaults to `otlp`. - - Can be set to `none` to disable automatic tracer initialization. - -You can pass multiple values to configure multiple exporters e.g, ``zipkin,prometheus`` - -Well known trace exporter names: - - - jaeger - - opencensus - - otlp - - otlp_span - - otlp_metric - - zipkin - -``otlp`` is an alias for ``otlp_span,otlp_metric``. - -* ``--service-name`` or ``OTEL_SERVICE_NAME`` - -When present the value is passed on to the relevant exporter initializer as ``service_name`` argument. - -* ``--ids-generator`` or ``OTEL_IDS_GENERATOR`` - -Used to specify which IDs Generator to use for the global Tracer Provider. By default, it -will use the random IDs generator. - -The code in ``program.py`` needs to use one of the packages for which there is -an OpenTelemetry integration. For a list of the available integrations please -check `here `_ - -Examples -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -:: - - opentelemetry-instrument -e otlp flask run --port=3000 - -The above command will pass ``-e otlp`` to the instrument command and ``--port=3000`` to ``flask run``. - -:: - - opentelemetry-instrument -e zipkin,otlp celery -A tasks worker --loglevel=info - -The above command will configure global trace provider, attach zipkin and otlp exporters to it and then -start celery with the rest of the arguments. - -:: - - opentelemetry-instrument --ids-generator random flask run --port=3000 - -The above command will configure the global trace provider to use the Random IDs Generator, and then -pass ``--port=3000`` to ``flask run``. +This package provides entrypoints to configure OpenTelemetry References ---------- From a3a4e3f61c87d0bb61823fa2e393b8284b71c07f Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Tue, 12 Jan 2021 14:25:38 -0800 Subject: [PATCH 06/12] fix test name, add test --- opentelemetry-distro/tests/test_distro.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/opentelemetry-distro/tests/test_distro.py b/opentelemetry-distro/tests/test_distro.py index 6c6a694161..f007cb0ba0 100644 --- a/opentelemetry-distro/tests/test_distro.py +++ b/opentelemetry-distro/tests/test_distro.py @@ -13,15 +13,23 @@ # limitations under the License. # type: ignore +import os + from unittest import TestCase from pkg_resources import DistributionNotFound, require +from opentelemetry.distro import OpenTelemetryDistro class TestDistribution(TestCase): - def test_proto(self): - + def test_package_available(self): try: require(["opentelemetry-distro"]) except DistributionNotFound: self.fail("opentelemetry-distro not installed") + + def test_default_configuration(self): + distro = OpenTelemetryDistro() + self.assertIsNone(os.environ.get("OTEL_EXPORTER")) + distro.configure() + self.assertEqual("otlp", os.environ.get("OTEL_EXPORTER")) \ No newline at end of file From 9148d6679bc8049aff8511b4498e31f876067723 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Wed, 13 Jan 2021 20:42:33 -0800 Subject: [PATCH 07/12] moving configurator into distro --- opentelemetry-distro/setup.cfg | 2 + .../src/opentelemetry/distro/__init__.py | 155 ++++++++++++++++ .../tests}/test_configurator.py | 10 +- opentelemetry-sdk/setup.cfg | 3 - .../sdk/configuration/__init__.py | 170 ------------------ 5 files changed, 162 insertions(+), 178 deletions(-) rename {opentelemetry-sdk/tests/configuration => opentelemetry-distro/tests}/test_configurator.py (94%) delete mode 100644 opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py diff --git a/opentelemetry-distro/setup.cfg b/opentelemetry-distro/setup.cfg index f7fd1b5bd3..a6609308d9 100644 --- a/opentelemetry-distro/setup.cfg +++ b/opentelemetry-distro/setup.cfg @@ -51,6 +51,8 @@ where = src [options.entry_points] opentelemetry_distro = otel_distro = opentelemetry.distro:OpenTelemetryDistro +opentelemetry_configurator = + otel_configurator = opentelemetry.distro:Configurator [options.extras_require] test = diff --git a/opentelemetry-distro/src/opentelemetry/distro/__init__.py b/opentelemetry-distro/src/opentelemetry/distro/__init__.py index 083881387e..eef28f1680 100644 --- a/opentelemetry-distro/src/opentelemetry/distro/__init__.py +++ b/opentelemetry-distro/src/opentelemetry/distro/__init__.py @@ -13,8 +13,163 @@ # limitations under the License. # import os +from logging import getLogger +from typing import Sequence, Tuple +from pkg_resources import iter_entry_points + +from opentelemetry import trace +from opentelemetry.configuration import Configuration +from opentelemetry.instrumentation.configurator import BaseConfigurator from opentelemetry.instrumentation.distro import BaseDistro +from opentelemetry.sdk.metrics.export import MetricsExporter +from opentelemetry.sdk.resources import Resource +from opentelemetry.sdk.trace import TracerProvider +from opentelemetry.sdk.trace.export import ( + BatchExportSpanProcessor, + SpanExporter, +) + + +logger = getLogger(__file__) + + +EXPORTER_OTLP = "otlp" +EXPORTER_OTLP_SPAN = "otlp_span" +EXPORTER_OTLP_METRIC = "otlp_metric" +_DEFAULT_EXPORTER = "none" + +RANDOM_IDS_GENERATOR = "random" +_DEFAULT_IDS_GENERATOR = RANDOM_IDS_GENERATOR + + +def _get_ids_generator() -> str: + return Configuration().IDS_GENERATOR or _DEFAULT_IDS_GENERATOR + + +def _get_service_name() -> str: + return Configuration().SERVICE_NAME or "" + + +def _get_exporter_names() -> Sequence[str]: + exporter = Configuration().EXPORTER or _DEFAULT_EXPORTER + if exporter.lower().strip() == "none": + return [] + + names = [] + for exp in exporter.split(","): + name = exp.strip() + if name == EXPORTER_OTLP: + names.append(EXPORTER_OTLP_SPAN) + names.append(EXPORTER_OTLP_METRIC) + else: + names.append(name) + return names + + +def _init_tracing( + exporters: Sequence[SpanExporter], ids_generator: trace.IdsGenerator +): + service_name = _get_service_name() + provider = TracerProvider( + resource=Resource.create({"service.name": service_name}), + ids_generator=ids_generator(), + ) + trace.set_tracer_provider(provider) + + for exporter_name, exporter_class in exporters.items(): + exporter_args = {} + if exporter_name not in [ + EXPORTER_OTLP, + EXPORTER_OTLP_SPAN, + ]: + exporter_args["service_name"] = service_name + + provider.add_span_processor( + BatchExportSpanProcessor(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], +) -> Tuple[Sequence[SpanExporter], Sequence[MetricsExporter]]: + trace_exporters, metric_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 + elif issubclass(exporter_impl, MetricsExporter): + metric_exporters[exporter_name] = exporter_impl + else: + raise RuntimeError( + "{0} is neither a trace exporter nor a metric exporter".format( + exporter_name + ) + ) + return trace_exporters, metric_exporters + + +def _import_ids_generator(ids_generator_name: str) -> trace.IdsGenerator: + # pylint: disable=unbalanced-tuple-unpacking + [ + (ids_generator_name, ids_generator_impl) + ] = _import_tracer_provider_config_components( + [ids_generator_name.strip()], "opentelemetry_ids_generator" + ) + + if issubclass(ids_generator_impl, trace.IdsGenerator): + return ids_generator_impl + + raise RuntimeError("{0} is not an IdsGenerator".format(ids_generator_name)) + + +def _initialize_components(): + exporter_names = _get_exporter_names() + trace_exporters, metric_exporters = _import_exporters(exporter_names) + ids_generator_name = _get_ids_generator() + ids_generator = _import_ids_generator(ids_generator_name) + _init_tracing(trace_exporters, ids_generator) + # We don't support automatic initialization for metric yet but have added + # some boilerplate in order to make sure current implementation does not + # lock us out of supporting metrics later without major surgery. + _init_metrics(metric_exporters) + + +def _init_metrics(exporters: Sequence[MetricsExporter]): + if exporters: + logger.warning("automatic metric initialization is not supported yet.") + + +class Configurator(BaseConfigurator): + def _configure(self, **kwargs): + _initialize_components() class OpenTelemetryDistro(BaseDistro): diff --git a/opentelemetry-sdk/tests/configuration/test_configurator.py b/opentelemetry-distro/tests/test_configurator.py similarity index 94% rename from opentelemetry-sdk/tests/configuration/test_configurator.py rename to opentelemetry-distro/tests/test_configurator.py index ebd6768444..90ee7fe931 100644 --- a/opentelemetry-sdk/tests/configuration/test_configurator.py +++ b/opentelemetry-distro/tests/test_configurator.py @@ -18,7 +18,7 @@ from unittest.mock import patch from opentelemetry.configuration import Configuration -from opentelemetry.sdk.configuration import ( +from opentelemetry.distro import ( _get_ids_generator, _import_ids_generator, _init_tracing, @@ -78,11 +78,10 @@ class TestTraceInit(TestCase): def setUp(self): super() self.get_provider_patcher = patch( - "opentelemetry.sdk.configuration.TracerProvider", Provider, + "opentelemetry.distro.TracerProvider", Provider ) self.get_processor_patcher = patch( - "opentelemetry.sdk.configuration.BatchExportSpanProcessor", - Processor, + "opentelemetry.distro.BatchExportSpanProcessor", Processor ) self.set_provider_patcher = patch( "opentelemetry.trace.set_tracer_provider" @@ -133,7 +132,8 @@ def test_trace_init_otlp(self): del environ["OTEL_SERVICE_NAME"] @patch.dict(environ, {"OTEL_IDS_GENERATOR": "custom_ids_generator"}) - @patch("opentelemetry.sdk.configuration.iter_entry_points") + @patch("opentelemetry.distro.IdsGenerator", new=IdsGenerator) + @patch("opentelemetry.distro.iter_entry_points") def test_trace_init_custom_ids_generator(self, mock_iter_entry_points): mock_iter_entry_points.configure_mock( return_value=[ diff --git a/opentelemetry-sdk/setup.cfg b/opentelemetry-sdk/setup.cfg index f7ad42ba99..b1ecb357d2 100644 --- a/opentelemetry-sdk/setup.cfg +++ b/opentelemetry-sdk/setup.cfg @@ -43,7 +43,6 @@ zip_safe = False include_package_data = True install_requires = opentelemetry-api == 0.17.dev0 - opentelemetry-instrumentation == 0.17.dev0 [options.packages.find] where = src @@ -56,8 +55,6 @@ opentelemetry_tracer_provider = opentelemetry_exporter = console_span = opentelemetry.sdk.trace.export:ConsoleSpanExporter console_metrics = opentelemetry.sdk.metrics.export:ConsoleMetricsExporter -opentelemetry_configurator = - sdk_configurator = opentelemetry.sdk.configuration:Configurator opentelemetry_ids_generator = random = opentelemetry.sdk.trace.ids_generator:RandomIdsGenerator diff --git a/opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py b/opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py deleted file mode 100644 index 61b855fc5b..0000000000 --- a/opentelemetry-sdk/src/opentelemetry/sdk/configuration/__init__.py +++ /dev/null @@ -1,170 +0,0 @@ -# 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 typing import Sequence, Tuple - -from pkg_resources import iter_entry_points - -from opentelemetry import trace -from opentelemetry.configuration import Configuration -from opentelemetry.instrumentation.configurator import BaseConfigurator -from opentelemetry.sdk.metrics.export import MetricsExporter -from opentelemetry.sdk.resources import Resource -from opentelemetry.sdk.trace import TracerProvider -from opentelemetry.sdk.trace.export import ( - BatchExportSpanProcessor, - SpanExporter, -) -from opentelemetry.sdk.trace.ids_generator import IdsGenerator - -logger = getLogger(__file__) - -EXPORTER_OTLP = "otlp" -EXPORTER_OTLP_SPAN = "otlp_span" -EXPORTER_OTLP_METRIC = "otlp_metric" -_DEFAULT_EXPORTER = "none" - -RANDOM_IDS_GENERATOR = "random" -_DEFAULT_IDS_GENERATOR = RANDOM_IDS_GENERATOR - - -def _get_ids_generator() -> str: - return Configuration().IDS_GENERATOR or _DEFAULT_IDS_GENERATOR - - -def _get_service_name() -> str: - return Configuration().SERVICE_NAME or "" - - -def _get_exporter_names() -> Sequence[str]: - exporter = Configuration().EXPORTER or _DEFAULT_EXPORTER - if exporter.lower().strip() == "none": - return [] - - names = [] - for exp in exporter.split(","): - name = exp.strip() - if name == EXPORTER_OTLP: - names.append(EXPORTER_OTLP_SPAN) - names.append(EXPORTER_OTLP_METRIC) - else: - names.append(name) - return names - - -def _init_tracing( - exporters: Sequence[SpanExporter], ids_generator: IdsGenerator -): - service_name = _get_service_name() - provider = TracerProvider( - resource=Resource.create({"service.name": service_name}), - ids_generator=ids_generator(), - ) - trace.set_tracer_provider(provider) - - for exporter_name, exporter_class in exporters.items(): - exporter_args = {} - if exporter_name not in [ - EXPORTER_OTLP, - EXPORTER_OTLP_SPAN, - ]: - exporter_args["service_name"] = service_name - - provider.add_span_processor( - BatchExportSpanProcessor(exporter_class(**exporter_args)) - ) - - -def _init_metrics(exporters: Sequence[MetricsExporter]): - if exporters: - logger.warning("automatic metric initialization is not supported yet.") - - -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], -) -> Tuple[Sequence[SpanExporter], Sequence[MetricsExporter]]: - trace_exporters, metric_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 - elif issubclass(exporter_impl, MetricsExporter): - metric_exporters[exporter_name] = exporter_impl - else: - raise RuntimeError( - "{0} is neither a trace exporter nor a metric exporter".format( - exporter_name - ) - ) - return trace_exporters, metric_exporters - - -def _import_ids_generator(ids_generator_name: str) -> IdsGenerator: - # pylint: disable=unbalanced-tuple-unpacking - [ - (ids_generator_name, ids_generator_impl) - ] = _import_tracer_provider_config_components( - [ids_generator_name.strip()], "opentelemetry_ids_generator" - ) - - if issubclass(ids_generator_impl, IdsGenerator): - return ids_generator_impl - - raise RuntimeError("{0} is not an IdsGenerator".format(ids_generator_name)) - - -def _initialize_components(): - exporter_names = _get_exporter_names() - trace_exporters, metric_exporters = _import_exporters(exporter_names) - ids_generator_name = _get_ids_generator() - ids_generator = _import_ids_generator(ids_generator_name) - _init_tracing(trace_exporters, ids_generator) - - # We don't support automatic initialization for metric yet but have added - # some boilerplate in order to make sure current implementation does not - # lock us out of supporting metrics later without major surgery. - _init_metrics(metric_exporters) - - -class Configurator(BaseConfigurator): - def _configure(self, **kwargs): - _initialize_components() From 37e14bfd13fa3a4641e9d0f6d248e3034019bc33 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Wed, 13 Jan 2021 21:12:58 -0800 Subject: [PATCH 08/12] fix lint --- opentelemetry-distro/src/opentelemetry/distro/__init__.py | 8 ++++---- opentelemetry-distro/tests/test_distro.py | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/opentelemetry-distro/src/opentelemetry/distro/__init__.py b/opentelemetry-distro/src/opentelemetry/distro/__init__.py index eef28f1680..f61232cfc5 100644 --- a/opentelemetry-distro/src/opentelemetry/distro/__init__.py +++ b/opentelemetry-distro/src/opentelemetry/distro/__init__.py @@ -29,7 +29,7 @@ BatchExportSpanProcessor, SpanExporter, ) - +from opentelemetry.sdk.trace.ids_generator import IdsGenerator logger = getLogger(__file__) @@ -68,7 +68,7 @@ def _get_exporter_names() -> Sequence[str]: def _init_tracing( - exporters: Sequence[SpanExporter], ids_generator: trace.IdsGenerator + exporters: Sequence[SpanExporter], ids_generator: IdsGenerator ): service_name = _get_service_name() provider = TracerProvider( @@ -136,7 +136,7 @@ def _import_exporters( return trace_exporters, metric_exporters -def _import_ids_generator(ids_generator_name: str) -> trace.IdsGenerator: +def _import_ids_generator(ids_generator_name: str) -> IdsGenerator: # pylint: disable=unbalanced-tuple-unpacking [ (ids_generator_name, ids_generator_impl) @@ -144,7 +144,7 @@ def _import_ids_generator(ids_generator_name: str) -> trace.IdsGenerator: [ids_generator_name.strip()], "opentelemetry_ids_generator" ) - if issubclass(ids_generator_impl, trace.IdsGenerator): + if issubclass(ids_generator_impl, IdsGenerator): return ids_generator_impl raise RuntimeError("{0} is not an IdsGenerator".format(ids_generator_name)) diff --git a/opentelemetry-distro/tests/test_distro.py b/opentelemetry-distro/tests/test_distro.py index f007cb0ba0..62d3a7e5e3 100644 --- a/opentelemetry-distro/tests/test_distro.py +++ b/opentelemetry-distro/tests/test_distro.py @@ -14,10 +14,10 @@ # type: ignore import os - from unittest import TestCase from pkg_resources import DistributionNotFound, require + from opentelemetry.distro import OpenTelemetryDistro @@ -32,4 +32,4 @@ def test_default_configuration(self): distro = OpenTelemetryDistro() self.assertIsNone(os.environ.get("OTEL_EXPORTER")) distro.configure() - self.assertEqual("otlp", os.environ.get("OTEL_EXPORTER")) \ No newline at end of file + self.assertEqual("otlp", os.environ.get("OTEL_EXPORTER")) From de6870157f476b7f9378fb792783213eea9185ee Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Mon, 18 Jan 2021 14:59:44 -0800 Subject: [PATCH 09/12] make otlp install optional --- opentelemetry-distro/setup.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opentelemetry-distro/setup.cfg b/opentelemetry-distro/setup.cfg index a6609308d9..f4dc2ef01e 100644 --- a/opentelemetry-distro/setup.cfg +++ b/opentelemetry-distro/setup.cfg @@ -43,7 +43,6 @@ include_package_data = True install_requires = opentelemetry-api == 0.17.dev0 opentelemetry-sdk == 0.17.dev0 - opentelemetry-exporter-otlp == 0.17.dev0 [options.packages.find] where = src @@ -56,3 +55,5 @@ opentelemetry_configurator = [options.extras_require] test = +otlp = + opentelemetry-exporter-otlp == 0.17.dev0 \ No newline at end of file From c871c521f6a5d933c1a3cfe88329f385d46a8a80 Mon Sep 17 00:00:00 2001 From: alrex Date: Mon, 18 Jan 2021 16:18:51 -0800 Subject: [PATCH 10/12] Apply suggestions from code review Co-authored-by: Diego Hurtado --- opentelemetry-distro/setup.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/opentelemetry-distro/setup.cfg b/opentelemetry-distro/setup.cfg index f4dc2ef01e..a9340e4608 100644 --- a/opentelemetry-distro/setup.cfg +++ b/opentelemetry-distro/setup.cfg @@ -49,11 +49,11 @@ where = src [options.entry_points] opentelemetry_distro = - otel_distro = opentelemetry.distro:OpenTelemetryDistro + distro = opentelemetry.distro:OpenTelemetryDistro opentelemetry_configurator = - otel_configurator = opentelemetry.distro:Configurator + configurator = opentelemetry.distro:Configurator [options.extras_require] test = otlp = - opentelemetry-exporter-otlp == 0.17.dev0 \ No newline at end of file + opentelemetry-exporter-otlp == 0.17.dev0 From 455b48a1e0fa3864f42a5428af5d75c5bd80f145 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Mon, 18 Jan 2021 20:37:59 -0800 Subject: [PATCH 11/12] cleanup tox --- tox.ini | 2 -- 1 file changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index f8f10f7d81..6f27fa0dcb 100644 --- a/tox.ini +++ b/tox.ini @@ -102,8 +102,6 @@ commands_pre = test: pip install {toxinidir}/opentelemetry-api {toxinidir}/opentelemetry-instrumentation {toxinidir}/opentelemetry-sdk {toxinidir}/tests/util test-core-proto: pip install {toxinidir}/opentelemetry-proto - distro: pip install {toxinidir}/opentelemetry-proto - distro: pip install {toxinidir}/exporter/opentelemetry-exporter-otlp distro: pip install {toxinidir}/opentelemetry-distro {toxinidir}/opentelemetry-distro instrumentation: pip install {toxinidir}/opentelemetry-instrumentation From 97ab4169b9bc0753e146a75363d307d3ca57e156 Mon Sep 17 00:00:00 2001 From: Alex Boten Date: Tue, 19 Jan 2021 08:14:49 -0800 Subject: [PATCH 12/12] remove unnecessary line --- opentelemetry-distro/src/opentelemetry/distro/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opentelemetry-distro/src/opentelemetry/distro/__init__.py b/opentelemetry-distro/src/opentelemetry/distro/__init__.py index f61232cfc5..8a48d256c7 100644 --- a/opentelemetry-distro/src/opentelemetry/distro/__init__.py +++ b/opentelemetry-distro/src/opentelemetry/distro/__init__.py @@ -37,7 +37,6 @@ EXPORTER_OTLP = "otlp" EXPORTER_OTLP_SPAN = "otlp_span" EXPORTER_OTLP_METRIC = "otlp_metric" -_DEFAULT_EXPORTER = "none" RANDOM_IDS_GENERATOR = "random" _DEFAULT_IDS_GENERATOR = RANDOM_IDS_GENERATOR @@ -52,7 +51,7 @@ def _get_service_name() -> str: def _get_exporter_names() -> Sequence[str]: - exporter = Configuration().EXPORTER or _DEFAULT_EXPORTER + exporter = Configuration().EXPORTER or EXPORTER_OTLP if exporter.lower().strip() == "none": return []