mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-30 13:43:03 +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/*'
|
- 'release/*'
|
||||||
pull_request:
|
pull_request:
|
||||||
env:
|
env:
|
||||||
CORE_REPO_SHA: 3e628b56f154d651816ba806b49940c6cc9a3556
|
CORE_REPO_SHA: 2ac247e8b666c6b5a735719ab78dc0cd94907d9b
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
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)
|
## [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
|
## [0.20b0](https://github.com/open-telemetry/opentelemetry-python-contrib/releases/tag/v0.20b0) - 2021-04-20
|
||||||
|
|
||||||
### Changed
|
### 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-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-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-sdk&subdirectory=opentelemetry-sdk"
|
||||||
-e "git+https://github.com/open-telemetry/opentelemetry-python.git#egg=opentelemetry-instrumentation&subdirectory=opentelemetry-instrumentation"
|
|
||||||
|
|
||||||
# Required by opentelemetry-instrumentation
|
# Required by opentelemetry-instrumentation
|
||||||
fastapi~=0.58.1
|
fastapi~=0.58.1
|
||||||
|
@ -24,6 +24,10 @@ from django.conf import settings
|
|||||||
|
|
||||||
settings.configure()
|
settings.configure()
|
||||||
|
|
||||||
|
source_dirs = [
|
||||||
|
os.path.abspath("../opentelemetry-instrumentation/src/"),
|
||||||
|
]
|
||||||
|
|
||||||
exp = "../exporter"
|
exp = "../exporter"
|
||||||
exp_dirs = [
|
exp_dirs = [
|
||||||
os.path.abspath("/".join(["../exporter", f, "src"]))
|
os.path.abspath("/".join(["../exporter", f, "src"]))
|
||||||
@ -45,7 +49,7 @@ sdk_ext_dirs = [
|
|||||||
if isdir(join(sdk_ext, f))
|
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 -----------------------------------------------------
|
# -- 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
|
mkdir -p $DISTDIR
|
||||||
rm -rf $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"
|
echo "building $d"
|
||||||
cd "$d"
|
cd "$d"
|
||||||
|
21
tox.ini
21
tox.ini
@ -5,6 +5,10 @@ envlist =
|
|||||||
; Environments are organized by individual package, allowing
|
; Environments are organized by individual package, allowing
|
||||||
; for specifying supported Python versions per package.
|
; 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
|
; opentelemetry-sdk-extension-aws
|
||||||
py3{6,7,8}-test-sdkextension-aws
|
py3{6,7,8}-test-sdkextension-aws
|
||||||
pypy3-test-sdkextension-aws
|
pypy3-test-sdkextension-aws
|
||||||
@ -172,6 +176,7 @@ deps =
|
|||||||
; FIXME: add mypy testing
|
; FIXME: add mypy testing
|
||||||
|
|
||||||
changedir =
|
changedir =
|
||||||
|
test-instrumentation-base: opentelemetry-instrumentation/tests
|
||||||
test-instrumentation-aiohttp-client: instrumentation/opentelemetry-instrumentation-aiohttp-client/tests
|
test-instrumentation-aiohttp-client: instrumentation/opentelemetry-instrumentation-aiohttp-client/tests
|
||||||
test-instrumentation-aiopg: instrumentation/opentelemetry-instrumentation-aiopg/tests
|
test-instrumentation-aiopg: instrumentation/opentelemetry-instrumentation-aiopg/tests
|
||||||
test-instrumentation-asgi: instrumentation/opentelemetry-instrumentation-asgi/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.
|
; 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-api
|
||||||
test: pip install {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions
|
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/opentelemetry-sdk
|
||||||
test: pip install {toxinidir}/opentelemetry-python-core/tests/util
|
test: pip install {toxinidir}/opentelemetry-python-core/tests/util
|
||||||
|
|
||||||
|
test: pip install {toxinidir}/opentelemetry-instrumentation
|
||||||
|
|
||||||
celery: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test]
|
celery: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test]
|
||||||
|
|
||||||
grpc: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc[test]
|
grpc: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc[test]
|
||||||
@ -286,7 +292,7 @@ commands_pre =
|
|||||||
|
|
||||||
sqlalchemy: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy
|
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
|
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
|
coverage: python {toxinidir}/scripts/eachdist.py install --editable
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
|
test: pwd
|
||||||
test: pytest {posargs}
|
test: pytest {posargs}
|
||||||
coverage: {toxinidir}/scripts/coverage.sh
|
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-api
|
||||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions
|
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-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
|
python -m pip install {toxinidir}/util/opentelemetry-util-http
|
||||||
|
|
||||||
changedir = docs
|
changedir = docs
|
||||||
@ -337,10 +344,10 @@ commands_pre =
|
|||||||
sudo apt-get install libsnappy-dev
|
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-api
|
||||||
python -m pip install {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions
|
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/opentelemetry-sdk
|
||||||
python -m pip install {toxinidir}/opentelemetry-python-core/tests/util
|
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-wsgi[test]
|
||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi[test]
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi[test]
|
||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-asgi[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]
|
python -m pip install -e {toxinidir}/propagator/opentelemetry-propagator-ot-trace[test]
|
||||||
|
|
||||||
commands =
|
commands =
|
||||||
python scripts/eachdist.py lint --check-only
|
python scripts/eachdist.py lint
|
||||||
|
|
||||||
[testenv:docker-tests]
|
[testenv:docker-tests]
|
||||||
deps =
|
deps =
|
||||||
@ -402,9 +409,9 @@ changedir =
|
|||||||
commands_pre =
|
commands_pre =
|
||||||
pip install -e {toxinidir}/opentelemetry-python-core/opentelemetry-api \
|
pip install -e {toxinidir}/opentelemetry-python-core/opentelemetry-api \
|
||||||
-e {toxinidir}/opentelemetry-python-core/opentelemetry-semantic-conventions \
|
-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/opentelemetry-sdk \
|
||||||
-e {toxinidir}/opentelemetry-python-core/tests/util \
|
-e {toxinidir}/opentelemetry-python-core/tests/util \
|
||||||
|
-e {toxinidir}/opentelemetry-instrumentation \
|
||||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg \
|
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-asyncpg \
|
||||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-celery \
|
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-celery \
|
||||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi \
|
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi \
|
||||||
|
Reference in New Issue
Block a user