From 48e65d70f2d5f2d5d144a490b40e8ce6cae87fdc Mon Sep 17 00:00:00 2001 From: Owais Lone Date: Tue, 25 May 2021 06:01:29 +0530 Subject: [PATCH] Simplify bootstrap and generate code - We now automatically generate bootstrap_gen.py file from the list of instrumentations present in the source tree. - Bootstrap command now uses consumes this auto-generated list instead of keeping it's own local copy. - We no longer uninstall packages before installing them as instrumentation package no longer specify libraries as dependencies so the edge cases are no longer there. - We no longer try to install an incompatible version or force upgrade/downgrade an installed version. This used to leave systems in broken states which should happen no more. --- .github/workflows/test.yml | 6 +- .gitignore | 1 + CHANGELOG.md | 4 + CONTRIBUTING.md | 2 +- dev-requirements.txt | 3 +- gen-requirements.txt | 4 + .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../setup.py | 30 ++- .../instrumentation/bootstrap.py | 173 ++++-------------- .../instrumentation/bootstrap_gen.py | 134 ++++++++++++++ .../tests/test_bootstrap.py | 49 +---- scripts/generate_instrumentation_bootstrap.py | 98 ++++++++++ scripts/license_header.txt | 13 ++ scripts/otel_packaging.py | 43 +++++ scripts/prepare_release.sh | 2 + templates/instrumentation_setup.py.txt | 26 +++ tox.ini | 11 ++ 47 files changed, 1316 insertions(+), 213 deletions(-) create mode 100644 gen-requirements.txt create mode 100644 opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py create mode 100755 scripts/generate_instrumentation_bootstrap.py create mode 100644 scripts/license_header.txt create mode 100644 scripts/otel_packaging.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 48d703c0a0..34856810e8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -76,7 +76,7 @@ jobs: strategy: fail-fast: false matrix: - tox-environment: [ "docker-tests", "lint", "docs" ] + tox-environment: [ "docker-tests", "lint", "docs", "generate" ] name: ${{ matrix.tox-environment }} runs-on: ubuntu-latest steps: @@ -102,3 +102,7 @@ jobs: key: v2-misc-tox-cache-${{ matrix.tox-environment }}-${{ hashFiles('tox.ini', 'dev-requirements.txt', 'docs-requirements.txt') }} - name: run tox run: tox -e ${{ matrix.tox-environment }} + - name: Ensure generated code is up to date + if: matrix.tox-environment == 'generate' + run: >- + git diff --exit-code || (echo 'Generated code is out of date, please run "tox -e generate" and commit the changes in this PR.' && exit 1) diff --git a/.gitignore b/.gitignore index cc3fb4d5e7..29118d428e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.egg *.egg-info dist +dist-info build eggs parts diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e680eef7..096feb9bb5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/open-telemetry/opentelemetry-python/compare/v1.2.0-0.21b0...HEAD) ### Changed +- `opentelemetry-bootstrap` not longer forcibly removes and re-installs libraries and their instrumentations. + This means running bootstrap will not auto-upgrade existing dependencies and as a result not cause dependency + conflicts. + ([#514](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/514)) - `opentelemetry-instrumentation-asgi` Set the response status code on the server span ([#478](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/478)) - `opentelemetry-instrumentation-tornado` Fixed cases where description was used with non- diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index da9a5a41eb..ed248a9e5d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -170,7 +170,7 @@ Below is a checklist of things to be mindful of when implementing a new instrume - Extends from [BaseInstrumentor](https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/opentelemetry-instrumentation/src/opentelemetry/instrumentation/instrumentor.py#L26) - Supports auto-instrumentation - Add an entry point (ex. https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-requests/setup.cfg#L56) - - Add instrumentation package to `bootstrap.py` (https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py#L37) + - Run `python scripts/setup.py` followed by `python scripts/generate_instrumentation_bootstrap.py` after adding a new instrumentation package. - Functionality that is common amongst other instrumentation and can be abstracted [here](https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/opentelemetry-instrumentation/src/opentelemetry/instrumentation) - Request/response [hooks](https://github.com/open-telemetry/opentelemetry-python-contrib/issues/408) for http instrumentations - `suppress_instrumentation` functionality diff --git a/dev-requirements.txt b/dev-requirements.txt index 75b9ed793b..241ec75579 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,7 +1,6 @@ +-r gen-requirements.txt pylint~=2.6 flake8~=3.7 -isort~=5.6 -black>=19.3b0,==19.* httpretty~=1.0 mypy==0.790 sphinx diff --git a/gen-requirements.txt b/gen-requirements.txt new file mode 100644 index 0000000000..2398eb8f29 --- /dev/null +++ b/gen-requirements.txt @@ -0,0 +1,4 @@ +astor~=0.8.1 +isort~=5.6 +black>=19.3b0,==19.* +jinja2~=2.7 \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.py b/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.py index f4bab26162..824db2983e 100644 --- a/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.py +++ b/instrumentation/opentelemetry-instrumentation-aiohttp-client/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-aiopg/setup.py b/instrumentation/opentelemetry-instrumentation-aiopg/setup.py index c1adb173a7..35b33958ef 100644 --- a/instrumentation/opentelemetry-instrumentation-aiopg/setup.py +++ b/instrumentation/opentelemetry-instrumentation-aiopg/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-asgi/setup.py b/instrumentation/opentelemetry-instrumentation-asgi/setup.py index b1c2111801..f89c65f0a2 100644 --- a/instrumentation/opentelemetry-instrumentation-asgi/setup.py +++ b/instrumentation/opentelemetry-instrumentation-asgi/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-asyncpg/setup.py b/instrumentation/opentelemetry-instrumentation-asyncpg/setup.py index 222715670e..785d822287 100644 --- a/instrumentation/opentelemetry-instrumentation-asyncpg/setup.py +++ b/instrumentation/opentelemetry-instrumentation-asyncpg/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-boto/setup.py b/instrumentation/opentelemetry-instrumentation-boto/setup.py index 847607a5dd..0d99cc8926 100644 --- a/instrumentation/opentelemetry-instrumentation-boto/setup.py +++ b/instrumentation/opentelemetry-instrumentation-boto/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-botocore/setup.py b/instrumentation/opentelemetry-instrumentation-botocore/setup.py index 2ee64c367a..5317443224 100644 --- a/instrumentation/opentelemetry-instrumentation-botocore/setup.py +++ b/instrumentation/opentelemetry-instrumentation-botocore/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-celery/setup.py b/instrumentation/opentelemetry-instrumentation-celery/setup.py index c0eaacd685..c4a01d212a 100644 --- a/instrumentation/opentelemetry-instrumentation-celery/setup.py +++ b/instrumentation/opentelemetry-instrumentation-celery/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-dbapi/setup.py b/instrumentation/opentelemetry-instrumentation-dbapi/setup.py index 5e4f9a8a14..2c1d28b951 100644 --- a/instrumentation/opentelemetry-instrumentation-dbapi/setup.py +++ b/instrumentation/opentelemetry-instrumentation-dbapi/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-django/setup.py b/instrumentation/opentelemetry-instrumentation-django/setup.py index 343be60893..5a425c0e5a 100644 --- a/instrumentation/opentelemetry-instrumentation-django/setup.py +++ b/instrumentation/opentelemetry-instrumentation-django/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.py b/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.py index 4292aa09a9..41914e50a8 100644 --- a/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.py +++ b/instrumentation/opentelemetry-instrumentation-elasticsearch/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/setup.py b/instrumentation/opentelemetry-instrumentation-falcon/setup.py index 6a59fd963c..ca770e9260 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/setup.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-fastapi/setup.py b/instrumentation/opentelemetry-instrumentation-fastapi/setup.py index 96d8bfb114..a56f7434a0 100644 --- a/instrumentation/opentelemetry-instrumentation-fastapi/setup.py +++ b/instrumentation/opentelemetry-instrumentation-fastapi/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-flask/setup.py b/instrumentation/opentelemetry-instrumentation-flask/setup.py index db6ffa90a2..251d905739 100644 --- a/instrumentation/opentelemetry-instrumentation-flask/setup.py +++ b/instrumentation/opentelemetry-instrumentation-flask/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-grpc/setup.py b/instrumentation/opentelemetry-instrumentation-grpc/setup.py index f99615ad9b..aaac587baa 100644 --- a/instrumentation/opentelemetry-instrumentation-grpc/setup.py +++ b/instrumentation/opentelemetry-instrumentation-grpc/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-jinja2/setup.py b/instrumentation/opentelemetry-instrumentation-jinja2/setup.py index 052cd4512b..f89c545429 100644 --- a/instrumentation/opentelemetry-instrumentation-jinja2/setup.py +++ b/instrumentation/opentelemetry-instrumentation-jinja2/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-logging/setup.py b/instrumentation/opentelemetry-instrumentation-logging/setup.py index 7bc16127a2..38f3826d33 100644 --- a/instrumentation/opentelemetry-instrumentation-logging/setup.py +++ b/instrumentation/opentelemetry-instrumentation-logging/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-mysql/setup.py b/instrumentation/opentelemetry-instrumentation-mysql/setup.py index 6c88dddc03..ba396f9ffb 100644 --- a/instrumentation/opentelemetry-instrumentation-mysql/setup.py +++ b/instrumentation/opentelemetry-instrumentation-mysql/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-psycopg2/setup.py b/instrumentation/opentelemetry-instrumentation-psycopg2/setup.py index 0f0bd9bb79..077f70345b 100644 --- a/instrumentation/opentelemetry-instrumentation-psycopg2/setup.py +++ b/instrumentation/opentelemetry-instrumentation-psycopg2/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-pymemcache/setup.py b/instrumentation/opentelemetry-instrumentation-pymemcache/setup.py index e3df52d7aa..2964e4c7a6 100644 --- a/instrumentation/opentelemetry-instrumentation-pymemcache/setup.py +++ b/instrumentation/opentelemetry-instrumentation-pymemcache/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-pymongo/setup.py b/instrumentation/opentelemetry-instrumentation-pymongo/setup.py index 04d8899ad8..23ea29503b 100644 --- a/instrumentation/opentelemetry-instrumentation-pymongo/setup.py +++ b/instrumentation/opentelemetry-instrumentation-pymongo/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-pymysql/setup.py b/instrumentation/opentelemetry-instrumentation-pymysql/setup.py index 9c18f42407..2da219349a 100644 --- a/instrumentation/opentelemetry-instrumentation-pymysql/setup.py +++ b/instrumentation/opentelemetry-instrumentation-pymysql/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-pyramid/setup.py b/instrumentation/opentelemetry-instrumentation-pyramid/setup.py index d334eb862c..20790df8c1 100644 --- a/instrumentation/opentelemetry-instrumentation-pyramid/setup.py +++ b/instrumentation/opentelemetry-instrumentation-pyramid/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-redis/setup.py b/instrumentation/opentelemetry-instrumentation-redis/setup.py index dabcea55d4..bc90b25aa8 100644 --- a/instrumentation/opentelemetry-instrumentation-redis/setup.py +++ b/instrumentation/opentelemetry-instrumentation-redis/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-requests/setup.py b/instrumentation/opentelemetry-instrumentation-requests/setup.py index e85711223a..c8e36d3b1f 100644 --- a/instrumentation/opentelemetry-instrumentation-requests/setup.py +++ b/instrumentation/opentelemetry-instrumentation-requests/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-sklearn/setup.py b/instrumentation/opentelemetry-instrumentation-sklearn/setup.py index 410bb0dfa0..8aec671b8d 100644 --- a/instrumentation/opentelemetry-instrumentation-sklearn/setup.py +++ b/instrumentation/opentelemetry-instrumentation-sklearn/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.py b/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.py index 18284583e5..90170b54a7 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.py +++ b/instrumentation/opentelemetry-instrumentation-sqlalchemy/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-sqlite3/setup.py b/instrumentation/opentelemetry-instrumentation-sqlite3/setup.py index 2386478785..1ea62e9c0a 100644 --- a/instrumentation/opentelemetry-instrumentation-sqlite3/setup.py +++ b/instrumentation/opentelemetry-instrumentation-sqlite3/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-starlette/setup.py b/instrumentation/opentelemetry-instrumentation-starlette/setup.py index 9d631fe63c..f7c28941c1 100644 --- a/instrumentation/opentelemetry-instrumentation-starlette/setup.py +++ b/instrumentation/opentelemetry-instrumentation-starlette/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-tornado/setup.py b/instrumentation/opentelemetry-instrumentation-tornado/setup.py index dca230b959..018b4773b3 100644 --- a/instrumentation/opentelemetry-instrumentation-tornado/setup.py +++ b/instrumentation/opentelemetry-instrumentation-tornado/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-urllib/setup.py b/instrumentation/opentelemetry-instrumentation-urllib/setup.py index 075453e34a..bebd64a09f 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib/setup.py +++ b/instrumentation/opentelemetry-instrumentation-urllib/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-urllib3/setup.py b/instrumentation/opentelemetry-instrumentation-urllib3/setup.py index 7517065c38..99c2c04396 100644 --- a/instrumentation/opentelemetry-instrumentation-urllib3/setup.py +++ b/instrumentation/opentelemetry-instrumentation-urllib3/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -66,6 +68,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/instrumentation/opentelemetry-instrumentation-wsgi/setup.py b/instrumentation/opentelemetry-instrumentation-wsgi/setup.py index 9d078200ac..2ee7cfbfcf 100644 --- a/instrumentation/opentelemetry-instrumentation-wsgi/setup.py +++ b/instrumentation/opentelemetry-instrumentation-wsgi/setup.py @@ -18,6 +18,8 @@ import os +import distutils.cmd +import json from configparser import ConfigParser import setuptools @@ -56,6 +58,32 @@ extras_require["test"] = test_deps + +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places", + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + "name": config["metadata"]["name"], + "version": PACKAGE_INFO["__version__"], + "instruments": PACKAGE_INFO["_instruments"], + } + print(json.dumps(metadata)) + + setuptools.setup( - version=PACKAGE_INFO["__version__"], extras_require=extras_require + cmdclass={"meta": JSONMetadataCommand}, + version=PACKAGE_INFO["__version__"], + extras_require=extras_require, ) diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py index 095189b974..e691f0c360 100644 --- a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap.py @@ -15,118 +15,19 @@ # limitations under the License. import argparse -import pkgutil +import logging import subprocess import sys -from logging import getLogger -from opentelemetry.instrumentation.version import __version__ as version - -logger = getLogger(__file__) - - -# A mapping of "target library" to "desired instrumentor path/versioned package -# name". Used as part of the `opentelemetry-bootstrap` command which looks at -# libraries used by the application that is to be instrumented, and handles -# automatically installing the appropriate instrumentations for that app. -# This helps for those who prefer to turn on as much instrumentation as -# possible, and don't want to go through the manual process of combing through -# the libraries their application uses to figure which one can be -# instrumented. -# NOTE: system-metrics is not to be included. -def all_instrumentations(): - pkg_instrumentation_map = { - "aiohttp-client": "opentelemetry-instrumentation-aiohttp-client", - "aiopg": "opentelemetry-instrumentation-aiopg", - "asyncpg": "opentelemetry-instrumentation-asyncpg", - "boto": "opentelemetry-instrumentation-boto", - "botocore": "opentelemetry-instrumentation-botocore", - "celery": "opentelemetry-instrumentation-celery", - "dbapi": "opentelemetry-instrumentation-dbapi", - "django": "opentelemetry-instrumentation-django", - "elasticsearch": "opentelemetry-instrumentation-elasticsearch", - "falcon": "opentelemetry-instrumentation-falcon", - "fastapi": "opentelemetry-instrumentation-fastapi", - "flask": "opentelemetry-instrumentation-flask", - "grpc": "opentelemetry-instrumentation-grpc", - "jinja2": "opentelemetry-instrumentation-jinja2", - "mysql": "opentelemetry-instrumentation-mysql", - "psycopg2": "opentelemetry-instrumentation-psycopg2", - "pymemcache": "opentelemetry-instrumentation-pymemcache", - "pymongo": "opentelemetry-instrumentation-pymongo", - "pymysql": "opentelemetry-instrumentation-pymysql", - "pyramid": "opentelemetry-instrumentation-pyramid", - "redis": "opentelemetry-instrumentation-redis", - "requests": "opentelemetry-instrumentation-requests", - "sklearn": "opentelemetry-instrumentation-sklearn", - "sqlalchemy": "opentelemetry-instrumentation-sqlalchemy", - "sqlite3": "opentelemetry-instrumentation-sqlite3", - "starlette": "opentelemetry-instrumentation-starlette", - "tornado": "opentelemetry-instrumentation-tornado", - "urllib": "opentelemetry-instrumentation-urllib", - } - for pkg, instrumentation in pkg_instrumentation_map.items(): - pkg_instrumentation_map[pkg] = "{0}=={1}".format( - instrumentation, version - ) - return pkg_instrumentation_map - - -instrumentations = all_instrumentations() - -# relevant instrumentors and tracers to uninstall and check for conflicts for target libraries -libraries = { - "aiohttp-client": ("opentelemetry-instrumentation-aiohttp-client",), - "aiopg": ("opentelemetry-instrumentation-aiopg",), - "asyncpg": ("opentelemetry-instrumentation-asyncpg",), - "boto": ("opentelemetry-instrumentation-boto",), - "botocore": ("opentelemetry-instrumentation-botocore",), - "celery": ("opentelemetry-instrumentation-celery",), - "dbapi": ("opentelemetry-instrumentation-dbapi",), - "django": ("opentelemetry-instrumentation-django",), - "elasticsearch": ("opentelemetry-instrumentation-elasticsearch",), - "falcon": ("opentelemetry-instrumentation-falcon",), - "fastapi": ("opentelemetry-instrumentation-fastapi",), - "flask": ("opentelemetry-instrumentation-flask",), - "grpc": ("opentelemetry-instrumentation-grpc",), - "jinja2": ("opentelemetry-instrumentation-jinja2",), - "mysql": ("opentelemetry-instrumentation-mysql",), - "psycopg2": ("opentelemetry-instrumentation-psycopg2",), - "pymemcache": ("opentelemetry-instrumentation-pymemcache",), - "pymongo": ("opentelemetry-instrumentation-pymongo",), - "pymysql": ("opentelemetry-instrumentation-pymysql",), - "pyramid": ("opentelemetry-instrumentation-pyramid",), - "redis": ("opentelemetry-instrumentation-redis",), - "requests": ("opentelemetry-instrumentation-requests",), - "sklearn": ("opentelemetry-instrumentation-sklearn",), - "sqlalchemy": ("opentelemetry-instrumentation-sqlalchemy",), - "sqlite3": ("opentelemetry-instrumentation-sqlite3",), - "starlette": ("opentelemetry-instrumentation-starlette",), - "tornado": ("opentelemetry-instrumentation-tornado",), - "urllib": ("opentelemetry-instrumentation-urllib",), -} - - -def _install_package(library, instrumentation): - """ - Ensures that desired version is installed w/o upgrading its dependencies - by uninstalling where necessary (if `target` is not provided). +import pkg_resources +from opentelemetry.instrumentation.bootstrap_gen import ( + default_instrumentations, + libraries, +) +from opentelemetry.instrumentation.version import __version__ as version - OpenTelemetry auto-instrumentation packages often have traced libraries - as instrumentation dependency (e.g. flask for - opentelemetry-instrumentation-flask), so using -I on library could cause - likely undesired Flask upgrade.Using --no-dependencies alone would leave - potential for nonfunctional installations. - """ - pip_list = _sys_pip_freeze() - for package in libraries[library]: - if "{}==".format(package).lower() in pip_list: - logger.info( - "Existing %s installation detected. Uninstalling.", package - ) - _sys_pip_uninstall(package) - _sys_pip_install(instrumentation) +logger = logging.getLogger(__file__) def _syscall(func): @@ -148,15 +49,6 @@ def wrapper(package=None): return wrapper -@_syscall -def _sys_pip_freeze(): - return ( - subprocess.check_output([sys.executable, "-m", "pip", "freeze"]) - .decode() - .lower() - ) - - @_syscall def _sys_pip_install(package): # explicit upgrade strategy to override potential pip config @@ -174,13 +66,6 @@ def _sys_pip_install(package): ) -@_syscall -def _sys_pip_uninstall(package): - subprocess.check_call( - [sys.executable, "-m", "pip", "uninstall", "-y", package] - ) - - def _pip_check(): """Ensures none of the instrumentations have dependency conflicts. Clean check reported as: @@ -203,22 +88,44 @@ def _pip_check(): ) -def _is_installed(library): - return library in sys.modules or pkgutil.find_loader(library) is not None +def _is_installed(req): + if req in sys.modules: + return True + + try: + pkg_resources.get_distribution(req) + except pkg_resources.DistributionNotFound: + return False + except pkg_resources.VersionConflict as exc: + logger.warning( + "instrumentation for package %s is available but version %s is installed. Skipping.", + exc.req, + exc.dist.as_requirement(), # pylint: disable=no-member + ) + return False + return True def _find_installed_libraries(): - return {k: v for k, v in instrumentations.items() if _is_installed(k)} - + libs = default_instrumentations[:] + libs.extend( + [ + v["instrumentation"] + for _, v in libraries.items() + if _is_installed(v["library"]) + ] + ) + return libs -def _run_requirements(packages): - print("\n".join(packages.values()), end="") +def _run_requirements(): + logger.setLevel(logging.ERROR) + print("\n".join(_find_installed_libraries()), end="") -def _run_install(packages): - for pkg, inst in packages.items(): - _install_package(pkg, inst) +def _run_install(): + for lib in _find_installed_libraries(): + _sys_pip_install(lib) _pip_check() @@ -250,4 +157,4 @@ def run() -> None: action_install: _run_install, action_requirements: _run_requirements, }[args.action] - cmd(_find_installed_libraries()) + cmd() diff --git a/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py new file mode 100644 index 0000000000..667f04d6cc --- /dev/null +++ b/opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py @@ -0,0 +1,134 @@ +# 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. + +# DO NOT EDIT. THIS FILE WAS AUTOGENERATED FROM INSTRUMENTATION PACKAGES. +# RUN `python scripts/generate_instrumentation_bootstrap.py` TO REGENERATE. + +libraries = { + "pymemcache": { + "library": "pymemcache ~= 1.3", + "instrumentation": "opentelemetry-instrumentation-pymemcache==0.22.dev0", + }, + "flask": { + "library": "flask ~= 1.0", + "instrumentation": "opentelemetry-instrumentation-flask==0.22.dev0", + }, + "scikit-learn": { + "library": "scikit-learn ~= 0.22.0", + "instrumentation": "opentelemetry-instrumentation-sklearn==0.22.dev0", + }, + "tornado": { + "library": "tornado >= 6.0", + "instrumentation": "opentelemetry-instrumentation-tornado==0.22.dev0", + }, + "psycopg2-binary": { + "library": "psycopg2-binary >= 2.7.3.1", + "instrumentation": "opentelemetry-instrumentation-psycopg2==0.22.dev0", + }, + "redis": { + "library": "redis >= 2.6", + "instrumentation": "opentelemetry-instrumentation-redis==0.22.dev0", + }, + "fastapi": { + "library": "fastapi ~= 0.58.1", + "instrumentation": "opentelemetry-instrumentation-fastapi==0.22.dev0", + }, + "starlette": { + "library": "starlette ~= 0.13.0", + "instrumentation": "opentelemetry-instrumentation-starlette==0.22.dev0", + }, + "celery": { + "library": "celery >= 4.0, < 6.0", + "instrumentation": "opentelemetry-instrumentation-celery==0.22.dev0", + }, + "urllib3": { + "library": "urllib3 >= 1.0.0, < 2.0.0", + "instrumentation": "opentelemetry-instrumentation-urllib3==0.22.dev0", + }, + "falcon": { + "library": "falcon ~= 2.0", + "instrumentation": "opentelemetry-instrumentation-falcon==0.22.dev0", + }, + "django": { + "library": "django >= 1.10", + "instrumentation": "opentelemetry-instrumentation-django==0.22.dev0", + }, + "botocore": { + "library": "botocore ~= 1.0", + "instrumentation": "opentelemetry-instrumentation-botocore==0.22.dev0", + }, + "requests": { + "library": "requests ~= 2.0", + "instrumentation": "opentelemetry-instrumentation-requests==0.22.dev0", + }, + "PyMySQL": { + "library": "PyMySQL ~= 0.10.1", + "instrumentation": "opentelemetry-instrumentation-pymysql==0.22.dev0", + }, + "aiopg": { + "library": "aiopg >= 0.13.0", + "instrumentation": "opentelemetry-instrumentation-aiopg==0.22.dev0", + }, + "asyncpg": { + "library": "asyncpg >= 0.12.0", + "instrumentation": "opentelemetry-instrumentation-asyncpg==0.22.dev0", + }, + "elasticsearch": { + "library": "elasticsearch >= 2.0", + "instrumentation": "opentelemetry-instrumentation-elasticsearch==0.22.dev0", + }, + "mysql-connector-python": { + "library": "mysql-connector-python ~= 8.0", + "instrumentation": "opentelemetry-instrumentation-mysql==0.22.dev0", + }, + "sqlalchemy": { + "library": "sqlalchemy", + "instrumentation": "opentelemetry-instrumentation-sqlalchemy==0.22.dev0", + }, + "aiohttp": { + "library": "aiohttp ~= 3.0", + "instrumentation": "opentelemetry-instrumentation-aiohttp-client==0.22.dev0", + }, + "jinja2": { + "library": "jinja2~=2.7", + "instrumentation": "opentelemetry-instrumentation-jinja2==0.22.dev0", + }, + "boto": { + "library": "boto~=2.0", + "instrumentation": "opentelemetry-instrumentation-boto==0.22.dev0", + }, + "pyramid": { + "library": "pyramid >= 1.7", + "instrumentation": "opentelemetry-instrumentation-pyramid==0.22.dev0", + }, + "asgiref": { + "library": "asgiref ~= 3.0", + "instrumentation": "opentelemetry-instrumentation-asgi==0.22.dev0", + }, + "pymongo": { + "library": "pymongo ~= 3.1", + "instrumentation": "opentelemetry-instrumentation-pymongo==0.22.dev0", + }, + "grpcio": { + "library": "grpcio ~= 1.27", + "instrumentation": "opentelemetry-instrumentation-grpc==0.22.dev0", + }, +} +default_instrumentations = [ + "opentelemetry-instrumentation-sqlite3==0.22.dev0", + "opentelemetry-instrumentation-logging==0.22.dev0", + "opentelemetry-instrumentation-wsgi==0.22.dev0", + "opentelemetry-instrumentation-urllib==0.22.dev0", + "opentelemetry-instrumentation-dbapi==0.22.dev0", +] diff --git a/opentelemetry-instrumentation/tests/test_bootstrap.py b/opentelemetry-instrumentation/tests/test_bootstrap.py index e5a1a86dda..d1052de289 100644 --- a/opentelemetry-instrumentation/tests/test_bootstrap.py +++ b/opentelemetry-instrumentation/tests/test_bootstrap.py @@ -13,18 +13,17 @@ # limitations under the License. # type: ignore -from functools import reduce from io import StringIO from random import sample from unittest import TestCase from unittest.mock import call, patch from opentelemetry.instrumentation import bootstrap +from opentelemetry.instrumentation.bootstrap_gen import libraries def sample_packages(packages, rate): - sampled = sample(list(packages), int(len(packages) * rate),) - return {k: v for k, v in packages.items() if k in sampled} + return sample(list(packages), int(len(packages) * rate),) class TestBootstrap(TestCase): @@ -34,9 +33,8 @@ class TestBootstrap(TestCase): @classmethod def setUpClass(cls): - # select random 60% of instrumentations cls.installed_libraries = sample_packages( - bootstrap.instrumentations, 0.6 + [lib["instrumentation"] for lib in libraries.values()], 0.6 ) # treat 50% of sampled packages as pre-installed @@ -49,39 +47,21 @@ def setUpClass(cls): return_value=cls.installed_libraries, ) - pip_freeze_output = [] - for inst in cls.installed_instrumentations.values(): - inst = inst.replace(">=", "==") - if "==" not in inst: - inst = "{}==x.y".format(inst) - pip_freeze_output.append(inst) - - cls.pip_freeze_patcher = patch( - "opentelemetry.instrumentation.bootstrap._sys_pip_freeze", - return_value="\n".join(pip_freeze_output), - ) cls.pip_install_patcher = patch( "opentelemetry.instrumentation.bootstrap._sys_pip_install", ) - cls.pip_uninstall_patcher = patch( - "opentelemetry.instrumentation.bootstrap._sys_pip_uninstall", - ) cls.pip_check_patcher = patch( "opentelemetry.instrumentation.bootstrap._pip_check", ) cls.pkg_patcher.start() - cls.mock_pip_freeze = cls.pip_freeze_patcher.start() cls.mock_pip_install = cls.pip_install_patcher.start() - cls.mock_pip_uninstall = cls.pip_uninstall_patcher.start() cls.mock_pip_check = cls.pip_check_patcher.start() @classmethod def tearDownClass(cls): cls.pip_check_patcher.start() - cls.pip_uninstall_patcher.start() cls.pip_install_patcher.start() - cls.pip_freeze_patcher.start() cls.pkg_patcher.stop() @patch("sys.argv", ["bootstrap", "-a", "pipenv"]) @@ -94,32 +74,13 @@ def test_run_cmd_print(self): with patch("sys.stdout", new=StringIO()) as fake_out: bootstrap.run() self.assertEqual( - fake_out.getvalue(), - "\n".join(self.installed_libraries.values()), + fake_out.getvalue(), "\n".join(self.installed_libraries), ) @patch("sys.argv", ["bootstrap", "-a", "install"]) def test_run_cmd_install(self): bootstrap.run() - - self.assertEqual( - self.mock_pip_freeze.call_count, len(self.installed_libraries) - ) - - to_uninstall = reduce( - lambda x, y: x + y, - [ - pkgs - for lib, pkgs in bootstrap.libraries.items() - if lib in self.installed_instrumentations - ], - ) - self.mock_pip_uninstall.assert_has_calls( - [call(i) for i in to_uninstall], any_order=True - ) - self.mock_pip_install.assert_has_calls( - [call(i) for i in self.installed_libraries.values()], - any_order=True, + [call(i) for i in self.installed_libraries], any_order=True, ) self.assertEqual(self.mock_pip_check.call_count, 1) diff --git a/scripts/generate_instrumentation_bootstrap.py b/scripts/generate_instrumentation_bootstrap.py new file mode 100755 index 0000000000..6d02ce6e5e --- /dev/null +++ b/scripts/generate_instrumentation_bootstrap.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +# 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 ast +import logging +import os +import subprocess + +import astor +import pkg_resources +from otel_packaging import ( + get_instrumentation_packages, + root_path, + scripts_path, +) + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("instrumentation_list_generator") + +_auto_generation_msg = """ +# DO NOT EDIT. THIS FILE WAS AUTOGENERATED FROM templates/{source}. +# RUN `python scripts/generate_setup.py` TO REGENERATE. +""" + +_template = """ +{header} + +# DO NOT EDIT. THIS FILE WAS AUTOGENERATED FROM INSTRUMENTATION PACKAGES. +# RUN `python scripts/generate_instrumentation_bootstrap.py` TO REGENERATE. + +{source} +""" + +_source_tmpl = """ +libraries = {} +default_instrumentations = [] +""" + + +gen_path = os.path.join( + root_path, + "opentelemetry-instrumentation", + "src", + "opentelemetry", + "instrumentation", + "bootstrap_gen.py", +) + + +def main(): + # pylint: disable=no-member + default_instrumentations = ast.List(elts=[]) + libraries = ast.Dict(keys=[], values=[]) + for pkg in get_instrumentation_packages(): + if not pkg["instruments"]: + default_instrumentations.elts.append(ast.Str(pkg["requirement"])) + for target_pkg in pkg["instruments"]: + parsed = pkg_resources.Requirement.parse(target_pkg) + libraries.keys.append(ast.Str(parsed.name)) + libraries.values.append( + ast.Dict( + keys=[ast.Str("library"), ast.Str("instrumentation")], + values=[ast.Str(target_pkg), ast.Str(pkg["requirement"])], + ) + ) + + tree = ast.parse(_source_tmpl) + tree.body[0].value = libraries + tree.body[1].value = default_instrumentations + source = astor.to_source(tree) + + with open( + os.path.join(scripts_path, "license_header.txt"), "r" + ) as header_file: + header = header_file.read() + source = _template.format(header=header, source=source) + + with open(gen_path, "w") as gen_file: + gen_file.write(source) + + subprocess.run(["black", "-q", gen_path], check=True) + + +if __name__ == "__main__": + main() diff --git a/scripts/license_header.txt b/scripts/license_header.txt new file mode 100644 index 0000000000..eacf7c9c0e --- /dev/null +++ b/scripts/license_header.txt @@ -0,0 +1,13 @@ +# 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. \ No newline at end of file diff --git a/scripts/otel_packaging.py b/scripts/otel_packaging.py new file mode 100644 index 0000000000..2fecee02e3 --- /dev/null +++ b/scripts/otel_packaging.py @@ -0,0 +1,43 @@ +# 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 json +import os +import subprocess + +scripts_path = os.path.dirname(os.path.abspath(__file__)) +root_path = os.path.dirname(scripts_path) +instrumentations_path = os.path.join(root_path, "instrumentation") + + +def get_instrumentation_packages(): + for pkg in os.listdir(instrumentations_path): + pkg_path = os.path.join(instrumentations_path, pkg) + if not os.path.isdir(pkg_path): + continue + + out = str( + subprocess.check_output( + "python setup.py meta", shell=True, cwd=pkg_path + ) + ) + instrumentation = json.loads(out.split("\\n")[1]) + instrumentation["requirement"] = "==".join( + (instrumentation["name"], instrumentation["version"],) + ) + yield instrumentation + + +if __name__ == "__main__": + print(list(get_instrumentation_packages())) diff --git a/scripts/prepare_release.sh b/scripts/prepare_release.sh index 607b6189bb..9a59d9122b 100755 --- a/scripts/prepare_release.sh +++ b/scripts/prepare_release.sh @@ -20,6 +20,8 @@ git checkout -b release/${VERSION} git push origin release/${VERSION} ./scripts/eachdist.py update_versions --versions stable,prerelease +./scripts/generate_setup.py +./scripts/generate_instrumentation_bootstrap.py rc=$? if [ $rc != 0 ]; then echo "::set-output name=version_updated::0" diff --git a/templates/instrumentation_setup.py.txt b/templates/instrumentation_setup.py.txt index 33607d9055..dfb13ffbdd 100644 --- a/templates/instrumentation_setup.py.txt +++ b/templates/instrumentation_setup.py.txt @@ -16,7 +16,9 @@ {{ auto_generation_msg }} +import distutils.cmd import os +import json from configparser import ConfigParser import setuptools @@ -55,7 +57,31 @@ for dep in extras_require["instruments"]: extras_require["test"] = test_deps +class JSONMetadataCommand(distutils.cmd.Command): + + description = ( + "print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ", + "auto-generate code in other places" + ) + user_options = [] + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + metadata = { + 'name': config['metadata']['name'], + 'version': PACKAGE_INFO['__version__'], + 'instruments': PACKAGE_INFO['_instruments'], + } + print(json.dumps(metadata)) + + setuptools.setup( + cmdclass={'meta': JSONMetadataCommand}, version=PACKAGE_INFO["__version__"], extras_require=extras_require ) \ No newline at end of file diff --git a/tox.ini b/tox.ini index f3bd555015..da2d06c856 100644 --- a/tox.ini +++ b/tox.ini @@ -156,6 +156,8 @@ envlist = docker-tests docs + generate + [testenv] deps = -c dev-requirements.txt @@ -430,3 +432,12 @@ commands = commands_post = docker-compose down -v + + +[testenv:generate] +deps = + -r {toxinidir}/gen-requirements.txt + +commands = + {toxinidir}/scripts/generate_setup.py + {toxinidir}/scripts/generate_instrumentation_bootstrap.py \ No newline at end of file