mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-29 21:23:55 +08:00
Move opentelemetry-instrumentation from core (#465)
This commit is contained in:
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -6,7 +6,7 @@ on:
|
||||
- 'release/*'
|
||||
pull_request:
|
||||
env:
|
||||
CORE_REPO_SHA: 3e628b56f154d651816ba806b49940c6cc9a3556
|
||||
CORE_REPO_SHA: 2ac247e8b666c6b5a735719ab78dc0cd94907d9b
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased](https://github.com/open-telemetry/opentelemetry-python-contrib/compare/v0.200...HEAD)
|
||||
|
||||
### Added
|
||||
- Move `opentelemetry-instrumentation` from core repository
|
||||
([#465](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/465))
|
||||
|
||||
## [0.20b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.20b0) - 2021-04-20
|
||||
|
||||
### Changed
|
||||
|
@ -7,7 +7,6 @@ sphinx-autodoc-typehints
|
||||
-e "git+https://github.com/open-telemetry/opentelemetry-python.git#egg=opentelemetry-api&subdirectory=opentelemetry-api"
|
||||
-e "git+https://github.com/open-telemetry/opentelemetry-python.git#egg=opentelemetry-semantic-conventions&subdirectory=opentelemetry-semantic-conventions"
|
||||
-e "git+https://github.com/open-telemetry/opentelemetry-python.git#egg=opentelemetry-sdk&subdirectory=opentelemetry-sdk"
|
||||
-e "git+https://github.com/open-telemetry/opentelemetry-python.git#egg=opentelemetry-instrumentation&subdirectory=opentelemetry-instrumentation"
|
||||
|
||||
# Required by opentelemetry-instrumentation
|
||||
fastapi~=0.58.1
|
||||
|
@ -24,6 +24,10 @@ from django.conf import settings
|
||||
|
||||
settings.configure()
|
||||
|
||||
source_dirs = [
|
||||
os.path.abspath("../opentelemetry-instrumentation/src/"),
|
||||
]
|
||||
|
||||
exp = "../exporter"
|
||||
exp_dirs = [
|
||||
os.path.abspath("/".join(["../exporter", f, "src"]))
|
||||
@ -45,7 +49,7 @@ sdk_ext_dirs = [
|
||||
if isdir(join(sdk_ext, f))
|
||||
]
|
||||
|
||||
sys.path[:0] = exp_dirs + instr_dirs + sdk_ext_dirs
|
||||
sys.path[:0] = source_dirs + exp_dirs + instr_dirs + sdk_ext_dirs
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
|
15
docs/instrumentation/base/instrumentation.rst
Normal file
15
docs/instrumentation/base/instrumentation.rst
Normal file
@ -0,0 +1,15 @@
|
||||
OpenTelemetry Python Instrumentor
|
||||
=================================
|
||||
|
||||
.. automodule:: opentelemetry.instrumentation
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
instrumentor
|
7
docs/instrumentation/base/instrumentor.rst
Normal file
7
docs/instrumentation/base/instrumentor.rst
Normal file
@ -0,0 +1,7 @@
|
||||
opentelemetry.instrumentation.instrumentor package
|
||||
==================================================
|
||||
|
||||
.. automodule:: opentelemetry.instrumentation.instrumentor
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
7
opentelemetry-instrumentation/MANIFEST.in
Normal file
7
opentelemetry-instrumentation/MANIFEST.in
Normal file
@ -0,0 +1,7 @@
|
||||
prune tests
|
||||
graft src
|
||||
global-exclude *.pyc
|
||||
global-exclude *.pyo
|
||||
global-exclude __pycache__/*
|
||||
include MANIFEST.in
|
||||
include README.rst
|
112
opentelemetry-instrumentation/README.rst
Normal file
112
opentelemetry-instrumentation/README.rst
Normal file
@ -0,0 +1,112 @@
|
||||
OpenTelemetry Instrumentation
|
||||
=============================
|
||||
|
||||
|pypi|
|
||||
|
||||
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation.svg
|
||||
:target: https://pypi.org/project/opentelemetry-instrumentation/
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
::
|
||||
|
||||
pip install opentelemetry-instrumentation
|
||||
|
||||
|
||||
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:
|
||||
|
||||
|
||||
* ``--trace-exporter`` or ``OTEL_TRACE_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_proto_grpc_span
|
||||
- zipkin
|
||||
|
||||
``otlp`` is an alias for ``otlp_proto_grpc_span``.
|
||||
|
||||
* ``--id-generator`` or ``OTEL_PYTHON_ID_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 <https://opentelemetry-python.readthedocs.io/en/stable/index.html#integrations>`_
|
||||
|
||||
* ``OTEL_PYTHON_DISABLED_INSTRUMENTATIONS``
|
||||
|
||||
If set by the user, opentelemetry-instrument will read this environment variable to disable specific instrumentations.
|
||||
e.g OTEL_PYTHON_DISABLED_INSTRUMENTATIONS = "requests,django"
|
||||
|
||||
|
||||
Examples
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
::
|
||||
|
||||
opentelemetry-instrument --trace-exporter otlp flask run --port=3000
|
||||
|
||||
The above command will pass ``--trace-exporter otlp`` to the instrument command and ``--port=3000`` to ``flask run``.
|
||||
|
||||
::
|
||||
|
||||
opentelemetry-instrument --trace-exporter 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 <https://opentelemetry.io/>`_
|
56
opentelemetry-instrumentation/setup.cfg
Normal file
56
opentelemetry-instrumentation/setup.cfg
Normal file
@ -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-instrumentation
|
||||
description = Instrumentation Tools & Auto Instrumentation for OpenTelemetry Python
|
||||
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-contrib/tree/main/opentelemetry-instrumentation
|
||||
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.6
|
||||
Programming Language :: Python :: 3.7
|
||||
Programming Language :: Python :: 3.8
|
||||
Programming Language :: Python :: 3.9
|
||||
|
||||
[options]
|
||||
python_requires = >=3.6
|
||||
package_dir=
|
||||
=src
|
||||
packages=find_namespace:
|
||||
zip_safe = False
|
||||
include_package_data = True
|
||||
install_requires =
|
||||
opentelemetry-api == 1.2.0.dev0
|
||||
wrapt >= 1.0.0, < 2.0.0
|
||||
|
||||
[options.packages.find]
|
||||
where = src
|
||||
|
||||
[options.entry_points]
|
||||
console_scripts =
|
||||
opentelemetry-instrument = opentelemetry.instrumentation.auto_instrumentation:run
|
||||
opentelemetry-bootstrap = opentelemetry.instrumentation.bootstrap:run
|
||||
|
||||
[options.extras_require]
|
||||
test =
|
29
opentelemetry-instrumentation/setup.py
Normal file
29
opentelemetry-instrumentation/setup.py
Normal file
@ -0,0 +1,29 @@
|
||||
# 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", "instrumentation", "version.py"
|
||||
)
|
||||
PACKAGE_INFO = {}
|
||||
with open(VERSION_FILENAME) as f:
|
||||
exec(f.read(), PACKAGE_INFO)
|
||||
|
||||
setuptools.setup(
|
||||
version=PACKAGE_INFO["__version__"],
|
||||
)
|
@ -0,0 +1,109 @@
|
||||
#!/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 argparse
|
||||
from logging import getLogger
|
||||
from os import environ, execl, getcwd
|
||||
from os.path import abspath, dirname, pathsep
|
||||
from shutil import which
|
||||
|
||||
from opentelemetry.environment_variables import (
|
||||
OTEL_PYTHON_ID_GENERATOR,
|
||||
OTEL_TRACES_EXPORTER,
|
||||
)
|
||||
|
||||
logger = getLogger(__file__)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="""
|
||||
opentelemetry-instrument automatically instruments a Python
|
||||
program and it's dependencies and then runs the program.
|
||||
"""
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--trace-exporter",
|
||||
required=False,
|
||||
help="""
|
||||
Uses the specified exporter to export spans.
|
||||
Accepts multiple exporters as comma separated values.
|
||||
|
||||
Examples:
|
||||
|
||||
--trace-exporter=jaeger
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--id-generator",
|
||||
required=False,
|
||||
help="""
|
||||
The IDs Generator to be used with the Tracer Provider.
|
||||
|
||||
Examples:
|
||||
|
||||
--id-generator=random
|
||||
""",
|
||||
)
|
||||
|
||||
parser.add_argument("command", help="Your Python application.")
|
||||
parser.add_argument(
|
||||
"command_args",
|
||||
help="Arguments for your application.",
|
||||
nargs=argparse.REMAINDER,
|
||||
)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def load_config_from_cli_args(args):
|
||||
if args.trace_exporter:
|
||||
environ[OTEL_TRACES_EXPORTER] = args.trace_exporter
|
||||
if args.id_generator:
|
||||
environ[OTEL_PYTHON_ID_GENERATOR] = args.id_generator
|
||||
|
||||
|
||||
def run() -> None:
|
||||
args = parse_args()
|
||||
load_config_from_cli_args(args)
|
||||
|
||||
python_path = environ.get("PYTHONPATH")
|
||||
|
||||
if not python_path:
|
||||
python_path = []
|
||||
|
||||
else:
|
||||
python_path = python_path.split(pathsep)
|
||||
|
||||
cwd_path = getcwd()
|
||||
|
||||
# This is being added to support applications that are being run from their
|
||||
# own executable, like Django.
|
||||
# FIXME investigate if there is another way to achieve this
|
||||
if cwd_path not in python_path:
|
||||
python_path.insert(0, cwd_path)
|
||||
|
||||
filedir_path = dirname(abspath(__file__))
|
||||
|
||||
python_path = [path for path in python_path if path != filedir_path]
|
||||
|
||||
python_path.insert(0, filedir_path)
|
||||
|
||||
environ["PYTHONPATH"] = pathsep.join(python_path)
|
||||
|
||||
executable = which(args.command)
|
||||
execl(executable, executable, *args.command_args)
|
@ -0,0 +1,109 @@
|
||||
# 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 sys
|
||||
from logging import getLogger
|
||||
from os import environ, path
|
||||
from os.path import abspath, dirname, pathsep
|
||||
from re import sub
|
||||
|
||||
from pkg_resources import iter_entry_points
|
||||
|
||||
from opentelemetry.environment_variables import (
|
||||
OTEL_PYTHON_DISABLED_INSTRUMENTATIONS,
|
||||
)
|
||||
|
||||
logger = getLogger(__file__)
|
||||
|
||||
|
||||
def _load_distros():
|
||||
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():
|
||||
package_to_exclude = environ.get(OTEL_PYTHON_DISABLED_INSTRUMENTATIONS, [])
|
||||
if isinstance(package_to_exclude, str):
|
||||
package_to_exclude = package_to_exclude.split(",")
|
||||
# to handle users entering "requests , flask" or "requests, flask" with spaces
|
||||
package_to_exclude = [x.strip() for x in package_to_exclude]
|
||||
|
||||
for entry_point in iter_entry_points("opentelemetry_instrumentor"):
|
||||
try:
|
||||
if entry_point.name in package_to_exclude:
|
||||
logger.debug(
|
||||
"Instrumentation skipped for library %s", entry_point.name
|
||||
)
|
||||
continue
|
||||
entry_point.load()().instrument() # type: ignore
|
||||
logger.debug("Instrumented %s", entry_point.name)
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
logger.exception("Instrumenting of %s failed", entry_point.name)
|
||||
raise exc
|
||||
|
||||
|
||||
def _load_configurators():
|
||||
configured = None
|
||||
for entry_point in iter_entry_points("opentelemetry_configurator"):
|
||||
if configured is not None:
|
||||
logger.warning(
|
||||
"Configuration of %s not loaded, %s already loaded",
|
||||
entry_point.name,
|
||||
configured,
|
||||
)
|
||||
continue
|
||||
try:
|
||||
entry_point.load()().configure() # type: ignore
|
||||
configured = entry_point.name
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
logger.exception("Configuration of %s failed", entry_point.name)
|
||||
raise exc
|
||||
|
||||
|
||||
def initialize():
|
||||
try:
|
||||
_load_distros()
|
||||
_load_configurators()
|
||||
_load_instrumentors()
|
||||
except Exception: # pylint: disable=broad-except
|
||||
logger.exception("Failed to auto initialize opentelemetry")
|
||||
finally:
|
||||
environ["PYTHONPATH"] = sub(
|
||||
r"{}{}?".format(dirname(abspath(__file__)), pathsep),
|
||||
"",
|
||||
environ["PYTHONPATH"],
|
||||
)
|
||||
|
||||
|
||||
if (
|
||||
hasattr(sys, "argv")
|
||||
and sys.argv[0].split(path.sep)[-1] == "celery"
|
||||
and "worker" in sys.argv[1:]
|
||||
):
|
||||
from celery.signals import worker_process_init # pylint:disable=E0401
|
||||
|
||||
@worker_process_init.connect(weak=False)
|
||||
def init_celery(*args, **kwargs):
|
||||
initialize()
|
||||
|
||||
|
||||
else:
|
||||
initialize()
|
@ -0,0 +1,253 @@
|
||||
#!/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 argparse
|
||||
import pkgutil
|
||||
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).
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def _syscall(func):
|
||||
def wrapper(package=None):
|
||||
try:
|
||||
if package:
|
||||
return func(package)
|
||||
return func()
|
||||
except subprocess.SubprocessError as exp:
|
||||
cmd = getattr(exp, "cmd", None)
|
||||
if cmd:
|
||||
msg = 'Error calling system command "{0}"'.format(
|
||||
" ".join(cmd)
|
||||
)
|
||||
if package:
|
||||
msg = '{0} for package "{1}"'.format(msg, package)
|
||||
raise RuntimeError(msg)
|
||||
|
||||
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
|
||||
subprocess.check_call(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pip",
|
||||
"install",
|
||||
"-U",
|
||||
"--upgrade-strategy",
|
||||
"only-if-needed",
|
||||
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:
|
||||
'No broken requirements found.'
|
||||
Dependency conflicts are reported as:
|
||||
'opentelemetry-instrumentation-flask 1.0.1 has requirement opentelemetry-sdk<2.0,>=1.0, but you have opentelemetry-sdk 0.5.'
|
||||
To not be too restrictive, we'll only check for relevant packages.
|
||||
"""
|
||||
# pylint: disable=consider-using-with
|
||||
check_pipe = subprocess.Popen(
|
||||
[sys.executable, "-m", "pip", "check"], stdout=subprocess.PIPE
|
||||
)
|
||||
pip_check = check_pipe.communicate()[0].decode()
|
||||
pip_check_lower = pip_check.lower()
|
||||
for package_tup in libraries.values():
|
||||
for package in package_tup:
|
||||
if package.lower() in pip_check_lower:
|
||||
raise RuntimeError(
|
||||
"Dependency conflict found: {}".format(pip_check)
|
||||
)
|
||||
|
||||
|
||||
def _is_installed(library):
|
||||
return library in sys.modules or pkgutil.find_loader(library) is not None
|
||||
|
||||
|
||||
def _find_installed_libraries():
|
||||
return {k: v for k, v in instrumentations.items() if _is_installed(k)}
|
||||
|
||||
|
||||
def _run_requirements(packages):
|
||||
print("\n".join(packages.values()), end="")
|
||||
|
||||
|
||||
def _run_install(packages):
|
||||
for pkg, inst in packages.items():
|
||||
_install_package(pkg, inst)
|
||||
|
||||
_pip_check()
|
||||
|
||||
|
||||
def run() -> None:
|
||||
action_install = "install"
|
||||
action_requirements = "requirements"
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="""
|
||||
opentelemetry-bootstrap detects installed libraries and automatically
|
||||
installs the relevant instrumentation packages for them.
|
||||
"""
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--action",
|
||||
choices=[action_install, action_requirements],
|
||||
default=action_requirements,
|
||||
help="""
|
||||
install - uses pip to install the new requirements using to the
|
||||
currently active site-package.
|
||||
requirements - prints out the new requirements to stdout. Action can
|
||||
be piped and appended to a requirements.txt file.
|
||||
""",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
cmd = {
|
||||
action_install: _run_install,
|
||||
action_requirements: _run_requirements,
|
||||
}[args.action]
|
||||
cmd(_find_installed_libraries())
|
@ -0,0 +1,53 @@
|
||||
# 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 Configurator
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from logging import getLogger
|
||||
|
||||
_LOG = getLogger(__name__)
|
||||
|
||||
|
||||
class BaseConfigurator(ABC):
|
||||
"""An ABC for configurators
|
||||
|
||||
Configurators are used to configure
|
||||
SDKs (i.e. TracerProvider, MeterProvider, Processors...)
|
||||
to reduce the amount of manual configuration required.
|
||||
"""
|
||||
|
||||
_instance = None
|
||||
_is_instrumented = False
|
||||
|
||||
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 SDK"""
|
||||
|
||||
def configure(self, **kwargs):
|
||||
"""Configure the SDK"""
|
||||
self._configure(**kwargs)
|
||||
|
||||
|
||||
__all__ = ["BaseConfigurator"]
|
@ -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"]
|
@ -0,0 +1,94 @@
|
||||
# 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 Instrumentor
|
||||
"""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from logging import getLogger
|
||||
|
||||
_LOG = getLogger(__name__)
|
||||
|
||||
|
||||
class BaseInstrumentor(ABC):
|
||||
"""An ABC for instrumentors
|
||||
|
||||
Child classes of this ABC should instrument specific third
|
||||
party libraries or frameworks either by using the
|
||||
``opentelemetry-instrument`` command or by calling their methods
|
||||
directly.
|
||||
|
||||
Since every third party library or framework is different and has different
|
||||
instrumentation needs, more methods can be added to the child classes as
|
||||
needed to provide practical instrumentation to the end user.
|
||||
"""
|
||||
|
||||
_instance = None
|
||||
_is_instrumented = False
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
|
||||
if cls._instance is None:
|
||||
cls._instance = object.__new__(cls, *args, **kwargs)
|
||||
|
||||
return cls._instance
|
||||
|
||||
@abstractmethod
|
||||
def _instrument(self, **kwargs):
|
||||
"""Instrument the library"""
|
||||
|
||||
@abstractmethod
|
||||
def _uninstrument(self, **kwargs):
|
||||
"""Uninstrument the library"""
|
||||
|
||||
def instrument(self, **kwargs):
|
||||
"""Instrument the library
|
||||
|
||||
This method will be called without any optional arguments by the
|
||||
``opentelemetry-instrument`` command.
|
||||
|
||||
This means that calling this method directly without passing any
|
||||
optional values should do the very same thing that the
|
||||
``opentelemetry-instrument`` command does.
|
||||
"""
|
||||
|
||||
if not self._is_instrumented:
|
||||
result = self._instrument(**kwargs)
|
||||
self._is_instrumented = True
|
||||
return result
|
||||
|
||||
_LOG.warning("Attempting to instrument while already instrumented")
|
||||
|
||||
return None
|
||||
|
||||
def uninstrument(self, **kwargs):
|
||||
"""Uninstrument the library
|
||||
|
||||
See ``BaseInstrumentor.instrument`` for more information regarding the
|
||||
usage of ``kwargs``.
|
||||
"""
|
||||
|
||||
if self._is_instrumented:
|
||||
result = self._uninstrument(**kwargs)
|
||||
self._is_instrumented = False
|
||||
return result
|
||||
|
||||
_LOG.warning("Attempting to uninstrument while already uninstrumented")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
__all__ = ["BaseInstrumentor"]
|
@ -0,0 +1,128 @@
|
||||
# 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.
|
||||
|
||||
"""
|
||||
This module implements experimental propagators to inject trace context
|
||||
into response carriers. This is useful for server side frameworks that start traces
|
||||
when server requests and want to share the trace context with the client so the
|
||||
client can add it's spans to the same trace.
|
||||
|
||||
This is part of an upcoming W3C spec and will eventually make it to the Otel spec.
|
||||
|
||||
https://w3c.github.io/trace-context/#trace-context-http-response-headers-format
|
||||
"""
|
||||
|
||||
import typing
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
import opentelemetry.trace as trace
|
||||
from opentelemetry.context.context import Context
|
||||
from opentelemetry.propagators import textmap
|
||||
from opentelemetry.trace import format_span_id, format_trace_id
|
||||
|
||||
_HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers"
|
||||
_RESPONSE_PROPAGATOR = None
|
||||
|
||||
|
||||
def get_global_response_propagator():
|
||||
return _RESPONSE_PROPAGATOR
|
||||
|
||||
|
||||
def set_global_response_propagator(propagator):
|
||||
global _RESPONSE_PROPAGATOR # pylint:disable=global-statement
|
||||
_RESPONSE_PROPAGATOR = propagator
|
||||
|
||||
|
||||
class Setter(ABC):
|
||||
@abstractmethod
|
||||
def set(self, carrier, key, value):
|
||||
"""Inject the provided key value pair in carrier."""
|
||||
|
||||
|
||||
class DictHeaderSetter(Setter):
|
||||
def set(self, carrier, key, value): # pylint: disable=no-self-use
|
||||
old_value = carrier.get(key, "")
|
||||
if old_value:
|
||||
value = "{0}, {1}".format(old_value, value)
|
||||
carrier[key] = value
|
||||
|
||||
|
||||
class FuncSetter(Setter):
|
||||
"""FuncSetter coverts a function into a valid Setter. Any function that can
|
||||
set values in a carrier can be converted into a Setter by using FuncSetter.
|
||||
This is useful when injecting trace context into non-dict objects such
|
||||
HTTP Response objects for different framework.
|
||||
|
||||
For example, it can be used to create a setter for Falcon response object as:
|
||||
|
||||
setter = FuncSetter(falcon.api.Response.append_header)
|
||||
|
||||
and then used with the propagator as:
|
||||
|
||||
propagator.inject(falcon_response, setter=setter)
|
||||
|
||||
This would essentially make the propagator call `falcon_response.append_header(key, value)`
|
||||
"""
|
||||
|
||||
def __init__(self, func):
|
||||
self._func = func
|
||||
|
||||
def set(self, carrier, key, value):
|
||||
self._func(carrier, key, value)
|
||||
|
||||
|
||||
default_setter = DictHeaderSetter()
|
||||
|
||||
|
||||
class ResponsePropagator(ABC):
|
||||
@abstractmethod
|
||||
def inject(
|
||||
self,
|
||||
carrier: textmap.CarrierT,
|
||||
context: typing.Optional[Context] = None,
|
||||
setter: textmap.Setter = default_setter,
|
||||
) -> None:
|
||||
"""Injects SpanContext into the HTTP response carrier."""
|
||||
|
||||
|
||||
class TraceResponsePropagator(ResponsePropagator):
|
||||
"""Experimental propagator that injects tracecontext into HTTP responses."""
|
||||
|
||||
def inject(
|
||||
self,
|
||||
carrier: textmap.CarrierT,
|
||||
context: typing.Optional[Context] = None,
|
||||
setter: textmap.Setter = default_setter,
|
||||
) -> None:
|
||||
"""Injects SpanContext into the HTTP response carrier."""
|
||||
span = trace.get_current_span(context)
|
||||
span_context = span.get_span_context()
|
||||
if span_context == trace.INVALID_SPAN_CONTEXT:
|
||||
return
|
||||
|
||||
header_name = "traceresponse"
|
||||
setter.set(
|
||||
carrier,
|
||||
header_name,
|
||||
"00-{trace_id}-{span_id}-{:02x}".format(
|
||||
span_context.trace_flags,
|
||||
trace_id=format_trace_id(span_context.trace_id),
|
||||
span_id=format_span_id(span_context.span_id),
|
||||
),
|
||||
)
|
||||
setter.set(
|
||||
carrier,
|
||||
_HTTP_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS,
|
||||
header_name,
|
||||
)
|
@ -0,0 +1,62 @@
|
||||
# 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 typing import Dict, Sequence
|
||||
|
||||
from wrapt import ObjectProxy
|
||||
|
||||
from opentelemetry.trace import StatusCode
|
||||
|
||||
|
||||
def extract_attributes_from_object(
|
||||
obj: any, attributes: Sequence[str], existing: Dict[str, str] = None
|
||||
) -> Dict[str, str]:
|
||||
extracted = {}
|
||||
if existing:
|
||||
extracted.update(existing)
|
||||
for attr in attributes:
|
||||
value = getattr(obj, attr, None)
|
||||
if value is not None:
|
||||
extracted[attr] = str(value)
|
||||
return extracted
|
||||
|
||||
|
||||
def http_status_to_status_code(
|
||||
status: int, allow_redirect: bool = True
|
||||
) -> StatusCode:
|
||||
"""Converts an HTTP status code to an OpenTelemetry canonical status code
|
||||
|
||||
Args:
|
||||
status (int): HTTP status code
|
||||
"""
|
||||
# See: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#status
|
||||
if status < 100:
|
||||
return StatusCode.ERROR
|
||||
if status <= 299:
|
||||
return StatusCode.UNSET
|
||||
if status <= 399 and allow_redirect:
|
||||
return StatusCode.UNSET
|
||||
return StatusCode.ERROR
|
||||
|
||||
|
||||
def unwrap(obj, attr: str):
|
||||
"""Given a function that was wrapped by wrapt.wrap_function_wrapper, unwrap it
|
||||
|
||||
Args:
|
||||
obj: Object that holds a reference to the wrapped function
|
||||
attr (str): Name of the wrapped function
|
||||
"""
|
||||
func = getattr(obj, attr, None)
|
||||
if func and isinstance(func, ObjectProxy) and hasattr(func, "__wrapped__"):
|
||||
setattr(obj, attr, func.__wrapped__)
|
@ -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.21.dev0"
|
0
opentelemetry-instrumentation/tests/__init__.py
Normal file
0
opentelemetry-instrumentation/tests/__init__.py
Normal file
128
opentelemetry-instrumentation/tests/test_bootstrap.py
Normal file
128
opentelemetry-instrumentation/tests/test_bootstrap.py
Normal file
@ -0,0 +1,128 @@
|
||||
# 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 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
|
||||
|
||||
|
||||
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}
|
||||
|
||||
|
||||
class TestBootstrap(TestCase):
|
||||
|
||||
installed_libraries = {}
|
||||
installed_instrumentations = {}
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# select random 60% of instrumentations
|
||||
cls.installed_libraries = sample_packages(
|
||||
bootstrap.instrumentations, 0.6
|
||||
)
|
||||
|
||||
# treat 50% of sampled packages as pre-installed
|
||||
cls.installed_instrumentations = sample_packages(
|
||||
cls.installed_libraries, 0.5
|
||||
)
|
||||
|
||||
cls.pkg_patcher = patch(
|
||||
"opentelemetry.instrumentation.bootstrap._find_installed_libraries",
|
||||
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"])
|
||||
def test_run_unknown_cmd(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
bootstrap.run()
|
||||
|
||||
@patch("sys.argv", ["bootstrap", "-a", "requirements"])
|
||||
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()),
|
||||
)
|
||||
|
||||
@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,
|
||||
)
|
||||
self.assertEqual(self.mock_pip_check.call_count, 1)
|
47
opentelemetry-instrumentation/tests/test_instrumentor.py
Normal file
47
opentelemetry-instrumentation/tests/test_instrumentor.py
Normal file
@ -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
|
||||
|
||||
from logging import WARNING
|
||||
from unittest import TestCase
|
||||
|
||||
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
||||
|
||||
|
||||
class TestInstrumentor(TestCase):
|
||||
class Instrumentor(BaseInstrumentor):
|
||||
def _instrument(self, **kwargs):
|
||||
return "instrumented"
|
||||
|
||||
def _uninstrument(self, **kwargs):
|
||||
return "uninstrumented"
|
||||
|
||||
def test_protect(self):
|
||||
instrumentor = self.Instrumentor()
|
||||
|
||||
with self.assertLogs(level=WARNING):
|
||||
self.assertIs(instrumentor.uninstrument(), None)
|
||||
|
||||
self.assertEqual(instrumentor.instrument(), "instrumented")
|
||||
|
||||
with self.assertLogs(level=WARNING):
|
||||
self.assertIs(instrumentor.instrument(), None)
|
||||
|
||||
self.assertEqual(instrumentor.uninstrument(), "uninstrumented")
|
||||
|
||||
with self.assertLogs(level=WARNING):
|
||||
self.assertIs(instrumentor.uninstrument(), None)
|
||||
|
||||
def test_singleton(self):
|
||||
self.assertIs(self.Instrumentor(), self.Instrumentor())
|
80
opentelemetry-instrumentation/tests/test_propagators.py
Normal file
80
opentelemetry-instrumentation/tests/test_propagators.py
Normal file
@ -0,0 +1,80 @@
|
||||
# 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.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.instrumentation import propagators
|
||||
from opentelemetry.instrumentation.propagators import (
|
||||
DictHeaderSetter,
|
||||
TraceResponsePropagator,
|
||||
get_global_response_propagator,
|
||||
set_global_response_propagator,
|
||||
)
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
|
||||
|
||||
class TestGlobals(TestBase):
|
||||
def test_get_set(self):
|
||||
original = propagators._RESPONSE_PROPAGATOR
|
||||
|
||||
propagators._RESPONSE_PROPAGATOR = None
|
||||
self.assertIsNone(get_global_response_propagator())
|
||||
|
||||
prop = TraceResponsePropagator()
|
||||
set_global_response_propagator(prop)
|
||||
self.assertIs(prop, get_global_response_propagator())
|
||||
|
||||
propagators._RESPONSE_PROPAGATOR = original
|
||||
|
||||
|
||||
class TestDictHeaderSetter(TestBase):
|
||||
def test_simple(self):
|
||||
setter = DictHeaderSetter()
|
||||
carrier = {}
|
||||
setter.set(carrier, "kk", "vv")
|
||||
self.assertIn("kk", carrier)
|
||||
self.assertEqual(carrier["kk"], "vv")
|
||||
|
||||
def test_append(self):
|
||||
setter = DictHeaderSetter()
|
||||
carrier = {"kk": "old"}
|
||||
setter.set(carrier, "kk", "vv")
|
||||
self.assertIn("kk", carrier)
|
||||
self.assertEqual(carrier["kk"], "old, vv")
|
||||
|
||||
|
||||
class TestTraceResponsePropagator(TestBase):
|
||||
def test_inject(self):
|
||||
span = trace.NonRecordingSpan(
|
||||
trace.SpanContext(
|
||||
trace_id=1,
|
||||
span_id=2,
|
||||
is_remote=False,
|
||||
trace_flags=trace.DEFAULT_TRACE_OPTIONS,
|
||||
trace_state=trace.DEFAULT_TRACE_STATE,
|
||||
),
|
||||
)
|
||||
|
||||
ctx = trace.set_span_in_context(span)
|
||||
prop = TraceResponsePropagator()
|
||||
carrier = {}
|
||||
prop.inject(carrier, ctx)
|
||||
self.assertEqual(
|
||||
carrier["Access-Control-Expose-Headers"], "traceresponse"
|
||||
)
|
||||
self.assertEqual(
|
||||
carrier["traceresponse"],
|
||||
"00-00000000000000000000000000000001-0000000000000002-00",
|
||||
)
|
117
opentelemetry-instrumentation/tests/test_run.py
Normal file
117
opentelemetry-instrumentation/tests/test_run.py
Normal file
@ -0,0 +1,117 @@
|
||||
# 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 os import environ, getcwd
|
||||
from os.path import abspath, dirname, pathsep
|
||||
from unittest import TestCase
|
||||
from unittest.mock import patch
|
||||
|
||||
from opentelemetry.environment_variables import OTEL_TRACES_EXPORTER
|
||||
from opentelemetry.instrumentation import auto_instrumentation
|
||||
|
||||
|
||||
class TestRun(TestCase):
|
||||
auto_instrumentation_path = dirname(abspath(auto_instrumentation.__file__))
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
cls.execl_patcher = patch(
|
||||
"opentelemetry.instrumentation.auto_instrumentation.execl"
|
||||
)
|
||||
cls.which_patcher = patch(
|
||||
"opentelemetry.instrumentation.auto_instrumentation.which"
|
||||
)
|
||||
|
||||
cls.execl_patcher.start()
|
||||
cls.which_patcher.start()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.execl_patcher.stop()
|
||||
cls.which_patcher.stop()
|
||||
|
||||
@patch("sys.argv", ["instrument", ""])
|
||||
@patch.dict("os.environ", {"PYTHONPATH": ""})
|
||||
def test_empty(self):
|
||||
auto_instrumentation.run()
|
||||
self.assertEqual(
|
||||
environ["PYTHONPATH"],
|
||||
pathsep.join([self.auto_instrumentation_path, getcwd()]),
|
||||
)
|
||||
|
||||
@patch("sys.argv", ["instrument", ""])
|
||||
@patch.dict("os.environ", {"PYTHONPATH": "abc"})
|
||||
def test_non_empty(self):
|
||||
auto_instrumentation.run()
|
||||
self.assertEqual(
|
||||
environ["PYTHONPATH"],
|
||||
pathsep.join([self.auto_instrumentation_path, getcwd(), "abc"]),
|
||||
)
|
||||
|
||||
@patch("sys.argv", ["instrument", ""])
|
||||
@patch.dict(
|
||||
"os.environ",
|
||||
{"PYTHONPATH": pathsep.join(["abc", auto_instrumentation_path])},
|
||||
)
|
||||
def test_after_path(self):
|
||||
auto_instrumentation.run()
|
||||
self.assertEqual(
|
||||
environ["PYTHONPATH"],
|
||||
pathsep.join([self.auto_instrumentation_path, getcwd(), "abc"]),
|
||||
)
|
||||
|
||||
@patch("sys.argv", ["instrument", ""])
|
||||
@patch.dict(
|
||||
"os.environ",
|
||||
{
|
||||
"PYTHONPATH": pathsep.join(
|
||||
[auto_instrumentation_path, "abc", auto_instrumentation_path]
|
||||
)
|
||||
},
|
||||
)
|
||||
def test_single_path(self):
|
||||
auto_instrumentation.run()
|
||||
self.assertEqual(
|
||||
environ["PYTHONPATH"],
|
||||
pathsep.join([self.auto_instrumentation_path, getcwd(), "abc"]),
|
||||
)
|
||||
|
||||
|
||||
class TestExecl(TestCase):
|
||||
@patch("sys.argv", ["1", "2", "3"])
|
||||
@patch("opentelemetry.instrumentation.auto_instrumentation.which")
|
||||
@patch("opentelemetry.instrumentation.auto_instrumentation.execl")
|
||||
def test_execl(
|
||||
self, mock_execl, mock_which
|
||||
): # pylint: disable=no-self-use
|
||||
mock_which.configure_mock(**{"return_value": "python"})
|
||||
|
||||
auto_instrumentation.run()
|
||||
|
||||
mock_execl.assert_called_with("python", "python", "3")
|
||||
|
||||
|
||||
class TestArgs(TestCase):
|
||||
@patch("opentelemetry.instrumentation.auto_instrumentation.execl")
|
||||
def test_exporter(self, _): # pylint: disable=no-self-use
|
||||
with patch("sys.argv", ["instrument", "2"]):
|
||||
auto_instrumentation.run()
|
||||
self.assertIsNone(environ.get(OTEL_TRACES_EXPORTER))
|
||||
|
||||
with patch(
|
||||
"sys.argv", ["instrument", "--trace-exporter", "jaeger", "1", "2"]
|
||||
):
|
||||
auto_instrumentation.run()
|
||||
self.assertEqual(environ.get(OTEL_TRACES_EXPORTER), "jaeger")
|
57
opentelemetry-instrumentation/tests/test_utils.py
Normal file
57
opentelemetry-instrumentation/tests/test_utils.py
Normal file
@ -0,0 +1,57 @@
|
||||
# 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 http import HTTPStatus
|
||||
|
||||
from opentelemetry.instrumentation.utils import http_status_to_status_code
|
||||
from opentelemetry.test.test_base import TestBase
|
||||
from opentelemetry.trace import StatusCode
|
||||
|
||||
|
||||
class TestUtils(TestBase):
|
||||
# See https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#status
|
||||
def test_http_status_to_status_code(self):
|
||||
for status_code, expected in (
|
||||
(HTTPStatus.OK, StatusCode.UNSET),
|
||||
(HTTPStatus.ACCEPTED, StatusCode.UNSET),
|
||||
(HTTPStatus.IM_USED, StatusCode.UNSET),
|
||||
(HTTPStatus.MULTIPLE_CHOICES, StatusCode.UNSET),
|
||||
(HTTPStatus.BAD_REQUEST, StatusCode.ERROR),
|
||||
(HTTPStatus.UNAUTHORIZED, StatusCode.ERROR),
|
||||
(HTTPStatus.FORBIDDEN, StatusCode.ERROR),
|
||||
(HTTPStatus.NOT_FOUND, StatusCode.ERROR),
|
||||
(
|
||||
HTTPStatus.UNPROCESSABLE_ENTITY,
|
||||
StatusCode.ERROR,
|
||||
),
|
||||
(
|
||||
HTTPStatus.TOO_MANY_REQUESTS,
|
||||
StatusCode.ERROR,
|
||||
),
|
||||
(HTTPStatus.NOT_IMPLEMENTED, StatusCode.ERROR),
|
||||
(HTTPStatus.SERVICE_UNAVAILABLE, StatusCode.ERROR),
|
||||
(
|
||||
HTTPStatus.GATEWAY_TIMEOUT,
|
||||
StatusCode.ERROR,
|
||||
),
|
||||
(
|
||||
HTTPStatus.HTTP_VERSION_NOT_SUPPORTED,
|
||||
StatusCode.ERROR,
|
||||
),
|
||||
(600, StatusCode.ERROR),
|
||||
(99, StatusCode.ERROR),
|
||||
):
|
||||
with self.subTest(status_code=status_code):
|
||||
actual = http_status_to_status_code(int(status_code))
|
||||
self.assertEqual(actual, expected, status_code)
|
@ -16,7 +16,7 @@ DISTDIR=dist
|
||||
mkdir -p $DISTDIR
|
||||
rm -rf $DISTDIR/*
|
||||
|
||||
for d in exporter/*/ instrumentation/*/ propagator/*/ sdk-extension/*/ util/*/ ; do
|
||||
for d in exporter/*/ instrumentation/*/ opentelemetry-instrumentation/*/ propagator/*/ sdk-extension/*/ util/*/ ; do
|
||||
(
|
||||
echo "building $d"
|
||||
cd "$d"
|
||||
|
21
tox.ini
21
tox.ini
@ -5,6 +5,10 @@ envlist =
|
||||
; Environments are organized by individual package, allowing
|
||||
; for specifying supported Python versions per package.
|
||||
|
||||
; opentelemetry-instrumentation
|
||||
py3{6,7,8,9}-test-instrumentation-base
|
||||
pypy3-test-instrumentation-base
|
||||
|
||||
; opentelemetry-sdk-extension-aws
|
||||
py3{6,7,8}-test-sdkextension-aws
|
||||
pypy3-test-sdkextension-aws
|
||||
@ -172,6 +176,7 @@ deps =
|
||||
; FIXME: add mypy testing
|
||||
|
||||
changedir =
|
||||
test-instrumentation-base: opentelemetry-instrumentation/tests
|
||||
test-instrumentation-aiohttp-client: instrumentation/opentelemetry-instrumentation-aiohttp-client/tests
|
||||
test-instrumentation-aiopg: instrumentation/opentelemetry-instrumentation-aiopg/tests
|
||||
test-instrumentation-asgi: instrumentation/opentelemetry-instrumentation-asgi/tests
|
||||
@ -217,10 +222,11 @@ commands_pre =
|
||||
; cases but it saves a lot of boilerplate in this file.
|
||||
test: pip install {toxinidir}/opentelemetry-python-core/opentelemetry-api
|
||||
test: pip install {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions
|
||||
test: pip install {toxinidir}/opentelemetry-python-core/opentelemetry-instrumentation
|
||||
test: pip install {toxinidir}/opentelemetry-python-core/opentelemetry-sdk
|
||||
test: pip install {toxinidir}/opentelemetry-python-core/tests/util
|
||||
|
||||
test: pip install {toxinidir}/opentelemetry-instrumentation
|
||||
|
||||
celery: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test]
|
||||
|
||||
grpc: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc[test]
|
||||
@ -286,7 +292,7 @@ commands_pre =
|
||||
|
||||
sqlalchemy: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy
|
||||
|
||||
elasticsearch{2,5,6,7}: pip install {toxinidir}/opentelemetry-python-core/opentelemetry-instrumentation {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch[test]
|
||||
elasticsearch{2,5,6,7}: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-elasticsearch[test]
|
||||
|
||||
aws: pip install requests {toxinidir}/sdk-extension/opentelemetry-sdk-extension-aws
|
||||
|
||||
@ -298,6 +304,7 @@ commands_pre =
|
||||
coverage: python {toxinidir}/scripts/eachdist.py install --editable
|
||||
|
||||
commands =
|
||||
test: pwd
|
||||
test: pytest {posargs}
|
||||
coverage: {toxinidir}/scripts/coverage.sh
|
||||
|
||||
@ -311,7 +318,7 @@ commands_pre =
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-api
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-sdk
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-instrumentation
|
||||
python -m pip install {toxinidir}/opentelemetry-instrumentation
|
||||
python -m pip install {toxinidir}/util/opentelemetry-util-http
|
||||
|
||||
changedir = docs
|
||||
@ -337,10 +344,10 @@ commands_pre =
|
||||
sudo apt-get install libsnappy-dev
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-api
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-instrumentation
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-sdk
|
||||
python -m pip install {toxinidir}/opentelemetry-python-core/tests/util
|
||||
python -m pip install {toxinidir}/util/opentelemetry-util-http
|
||||
python -m pip install -e {toxinidir}/opentelemetry-instrumentation[test]
|
||||
python -m pip install -e {toxinidir}/util/opentelemetry-util-http[test]
|
||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-wsgi[test]
|
||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi[test]
|
||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi[test]
|
||||
@ -378,7 +385,7 @@ commands_pre =
|
||||
python -m pip install -e {toxinidir}/propagator/opentelemetry-propagator-ot-trace[test]
|
||||
|
||||
commands =
|
||||
python scripts/eachdist.py lint --check-only
|
||||
python scripts/eachdist.py lint
|
||||
|
||||
[testenv:docker-tests]
|
||||
deps =
|
||||
@ -402,9 +409,9 @@ changedir =
|
||||
commands_pre =
|
||||
pip install -e {toxinidir}/opentelemetry-python-core/opentelemetry-api \
|
||||
-e {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions \
|
||||
-e {toxinidir}/opentelemetry-python-core/opentelemetry-instrumentation \
|
||||
-e {toxinidir}/opentelemetry-python-core/opentelemetry-sdk \
|
||||
-e {toxinidir}/opentelemetry-python-core/tests/util \
|
||||
-e {toxinidir}/opentelemetry-instrumentation \
|
||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg \
|
||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-celery \
|
||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi \
|
||||
|
Reference in New Issue
Block a user