mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-29 21:23:55 +08:00
Feature/add pika instrumentation (#680)
* Added initial code * Add all needed spans, and add support of instrumentation and uninstrumentation * Added tests. Ready for PR * Rename RequestsInstrumentation to RequestsInstrumentor to follow conventions * Add suppress_instrumentation functionality * Fix suppress_instrumentation functionality * Fix CR comments and lint test failures * Add usage of wrapt according to CR comments * Fix according to CR Comments * Move the tracer to be an attribute of the instrumentor instead of the channel * Fix Tests * Update Changelog and fix failing test * update code using tox -e generate * Update the name of the variable to store the tracer provider. * Update the core repo hash in the workflow * Update the core repo hash in the workflow Co-authored-by: Leighton Chen <lechen@microsoft.com> Co-authored-by: Diego Hurtado <ocelotl@users.noreply.github.com> Co-authored-by: Owais Lone <owais@users.noreply.github.com>
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: 10208c1be1e720925a80a66f711b8afbe67537f4
|
CORE_REPO_SHA: adad94bfa69520cb4cbabca714827fd14503baf0
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -53,6 +53,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-aiohttp-client`, `openetelemetry-instrumentation-fastapi`,
|
- `opentelemetry-instrumentation-asgi`, `opentelemetry-instrumentation-aiohttp-client`, `openetelemetry-instrumentation-fastapi`,
|
||||||
`opentelemetry-instrumentation-starlette`, `opentelemetry-instrumentation-urllib`, `opentelemetry-instrumentation-urllib3` Added `request_hook` and `response_hook` callbacks
|
`opentelemetry-instrumentation-starlette`, `opentelemetry-instrumentation-urllib`, `opentelemetry-instrumentation-urllib3` Added `request_hook` and `response_hook` callbacks
|
||||||
([#576](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/576))
|
([#576](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/576))
|
||||||
|
- `opentelemetry-instrumentation-pika` added RabbitMQ's pika module instrumentation.
|
||||||
|
([#680](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/680))
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
| [opentelemetry-instrumentation-jinja2](./opentelemetry-instrumentation-jinja2) | jinja2~=2.7 |
|
| [opentelemetry-instrumentation-jinja2](./opentelemetry-instrumentation-jinja2) | jinja2~=2.7 |
|
||||||
| [opentelemetry-instrumentation-logging](./opentelemetry-instrumentation-logging) | logging |
|
| [opentelemetry-instrumentation-logging](./opentelemetry-instrumentation-logging) | logging |
|
||||||
| [opentelemetry-instrumentation-mysql](./opentelemetry-instrumentation-mysql) | mysql-connector-python ~= 8.0 |
|
| [opentelemetry-instrumentation-mysql](./opentelemetry-instrumentation-mysql) | mysql-connector-python ~= 8.0 |
|
||||||
|
| [opentelemetry-instrumentation-pika](./opentelemetry-instrumentation-pika) | pika >= 1.1.0 |
|
||||||
| [opentelemetry-instrumentation-psycopg2](./opentelemetry-instrumentation-psycopg2) | psycopg2 >= 2.7.3.1 |
|
| [opentelemetry-instrumentation-psycopg2](./opentelemetry-instrumentation-psycopg2) | psycopg2 >= 2.7.3.1 |
|
||||||
| [opentelemetry-instrumentation-pymemcache](./opentelemetry-instrumentation-pymemcache) | pymemcache ~= 1.3 |
|
| [opentelemetry-instrumentation-pymemcache](./opentelemetry-instrumentation-pymemcache) | pymemcache ~= 1.3 |
|
||||||
| [opentelemetry-instrumentation-pymongo](./opentelemetry-instrumentation-pymongo) | pymongo ~= 3.1 |
|
| [opentelemetry-instrumentation-pymongo](./opentelemetry-instrumentation-pymongo) | pymongo ~= 3.1 |
|
||||||
|
@ -0,0 +1,70 @@
|
|||||||
|
OpenTelemetry pika Instrumentation
|
||||||
|
==================================
|
||||||
|
|
||||||
|
|pypi|
|
||||||
|
|
||||||
|
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-pika.svg
|
||||||
|
:target: https://pypi.org/project/opentelemetry-instrumentation-pika/
|
||||||
|
|
||||||
|
This library allows tracing requests made by the pika library.
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
pip install opentelemetry-instrumentation-pika
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Start broker backend
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
docker run -p 5672:5672 rabbitmq
|
||||||
|
|
||||||
|
* Run instrumented task
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pika
|
||||||
|
from opentelemetry.instrumentation.pika import PikaInstrumentor
|
||||||
|
|
||||||
|
PikaInstrumentor().instrument()
|
||||||
|
|
||||||
|
connection = pika.BlockingConnection(pika.URLParameters('amqp://localhost'))
|
||||||
|
channel = connection.channel()
|
||||||
|
channel.queue_declare(queue='hello')
|
||||||
|
channel.basic_publish(exchange='', routing_key='hello', body=b'Hello World!')
|
||||||
|
|
||||||
|
* PikaInstrumentor also supports instrumentation of a single channel
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pika
|
||||||
|
from opentelemetry.instrumentation.pika import PikaInstrumentor
|
||||||
|
|
||||||
|
connection = pika.BlockingConnection(pika.URLParameters('amqp://localhost'))
|
||||||
|
channel = connection.channel()
|
||||||
|
channel.queue_declare(queue='hello')
|
||||||
|
|
||||||
|
pika_instrumentation = PikaInstrumentor()
|
||||||
|
pika_instrumentation.instrument_channel(channel=channel)
|
||||||
|
|
||||||
|
|
||||||
|
channel.basic_publish(exchange='', routing_key='hello', body=b'Hello World!')
|
||||||
|
|
||||||
|
pika_instrumentation.uninstrument_channel(channel=channel)
|
||||||
|
|
||||||
|
* PikaInstrumentor also supports instrumentation without creating an object, and receiving a tracer_provider
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
PikaInstrumentor.instrument_channel(channel, tracer_provider=tracer_provider)
|
||||||
|
|
||||||
|
References
|
||||||
|
----------
|
||||||
|
|
||||||
|
* `OpenTelemetry pika/ Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/pika/pika.html>`_
|
||||||
|
* `OpenTelemetry Project <https://opentelemetry.io/>`_
|
56
instrumentation/opentelemetry-instrumentation-pika/setup.cfg
Normal file
56
instrumentation/opentelemetry-instrumentation-pika/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-pika
|
||||||
|
description = OpenTelemetry pika instrumentation
|
||||||
|
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/instrumentation/opentelemetry-instrumentation-pika
|
||||||
|
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
|
||||||
|
|
||||||
|
[options]
|
||||||
|
python_requires = >=3.6
|
||||||
|
package_dir=
|
||||||
|
=src
|
||||||
|
packages=find_namespace:
|
||||||
|
|
||||||
|
install_requires =
|
||||||
|
opentelemetry-api ~= 1.5
|
||||||
|
wrapt >= 1.0.0, < 2.0.0
|
||||||
|
|
||||||
|
[options.extras_require]
|
||||||
|
test =
|
||||||
|
pytest
|
||||||
|
wrapt >= 1.0.0, < 2.0.0
|
||||||
|
opentelemetry-test == 0.24b0
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
where = src
|
||||||
|
|
||||||
|
[options.entry_points]
|
||||||
|
opentelemetry_instrumentor =
|
||||||
|
pika = opentelemetry.instrumentation.pika:PikaInstrumentor
|
89
instrumentation/opentelemetry-instrumentation-pika/setup.py
Normal file
89
instrumentation/opentelemetry-instrumentation-pika/setup.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
# Copyright The OpenTelemetry Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
# DO NOT EDIT. THIS FILE WAS AUTOGENERATED FROM templates/instrumentation_setup.py.txt.
|
||||||
|
# RUN `python scripts/generate_setup.py` TO REGENERATE.
|
||||||
|
|
||||||
|
|
||||||
|
import distutils.cmd
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from configparser import ConfigParser
|
||||||
|
|
||||||
|
import setuptools
|
||||||
|
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read("setup.cfg")
|
||||||
|
|
||||||
|
# We provide extras_require parameter to setuptools.setup later which
|
||||||
|
# overwrites the extra_require section from setup.cfg. To support extra_require
|
||||||
|
# secion in setup.cfg, we load it here and merge it with the extra_require param.
|
||||||
|
extras_require = {}
|
||||||
|
if "options.extras_require" in config:
|
||||||
|
for key, value in config["options.extras_require"].items():
|
||||||
|
extras_require[key] = [v for v in value.split("\n") if v.strip()]
|
||||||
|
|
||||||
|
BASE_DIR = os.path.dirname(__file__)
|
||||||
|
PACKAGE_INFO = {}
|
||||||
|
|
||||||
|
VERSION_FILENAME = os.path.join(
|
||||||
|
BASE_DIR, "src", "opentelemetry", "instrumentation", "pika", "version.py"
|
||||||
|
)
|
||||||
|
with open(VERSION_FILENAME, encoding="utf-8") as f:
|
||||||
|
exec(f.read(), PACKAGE_INFO)
|
||||||
|
|
||||||
|
PACKAGE_FILENAME = os.path.join(
|
||||||
|
BASE_DIR, "src", "opentelemetry", "instrumentation", "pika", "package.py"
|
||||||
|
)
|
||||||
|
with open(PACKAGE_FILENAME, encoding="utf-8") as f:
|
||||||
|
exec(f.read(), PACKAGE_INFO)
|
||||||
|
|
||||||
|
# Mark any instruments/runtime dependencies as test dependencies as well.
|
||||||
|
extras_require["instruments"] = PACKAGE_INFO["_instruments"]
|
||||||
|
test_deps = extras_require.get("test", [])
|
||||||
|
for dep in extras_require["instruments"]:
|
||||||
|
test_deps.append(dep)
|
||||||
|
|
||||||
|
extras_require["test"] = test_deps
|
||||||
|
|
||||||
|
|
||||||
|
class JSONMetadataCommand(distutils.cmd.Command):
|
||||||
|
|
||||||
|
description = (
|
||||||
|
"print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ",
|
||||||
|
"auto-generate code in other places",
|
||||||
|
)
|
||||||
|
user_options = []
|
||||||
|
|
||||||
|
def initialize_options(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def finalize_options(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
metadata = {
|
||||||
|
"name": config["metadata"]["name"],
|
||||||
|
"version": PACKAGE_INFO["__version__"],
|
||||||
|
"instruments": PACKAGE_INFO["_instruments"],
|
||||||
|
}
|
||||||
|
print(json.dumps(metadata))
|
||||||
|
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
cmdclass={"meta": JSONMetadataCommand},
|
||||||
|
version=PACKAGE_INFO["__version__"],
|
||||||
|
extras_require=extras_require,
|
||||||
|
)
|
@ -0,0 +1,73 @@
|
|||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
Instrument `pika` to trace RabbitMQ applications.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
* Start broker backend
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
docker run -p 5672:5672 rabbitmq
|
||||||
|
|
||||||
|
* Run instrumented task
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pika
|
||||||
|
from opentelemetry.instrumentation.pika import PikaInstrumentor
|
||||||
|
|
||||||
|
PikaInstrumentor().instrument()
|
||||||
|
|
||||||
|
connection = pika.BlockingConnection(pika.URLParameters('amqp://localhost'))
|
||||||
|
channel = connection.channel()
|
||||||
|
channel.queue_declare(queue='hello')
|
||||||
|
channel.basic_publish(exchange='', routing_key='hello', body=b'Hello World!')
|
||||||
|
|
||||||
|
* PikaInstrumentor also supports instrumentation of a single channel
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import pika
|
||||||
|
from opentelemetry.instrumentation.pika import PikaInstrumentor
|
||||||
|
|
||||||
|
connection = pika.BlockingConnection(pika.URLParameters('amqp://localhost'))
|
||||||
|
channel = connection.channel()
|
||||||
|
channel.queue_declare(queue='hello')
|
||||||
|
|
||||||
|
pika_instrumentation = PikaInstrumentor()
|
||||||
|
pika_instrumentation.instrument_channel(channel=channel)
|
||||||
|
|
||||||
|
|
||||||
|
channel.basic_publish(exchange='', routing_key='hello', body=b'Hello World!')
|
||||||
|
|
||||||
|
pika_instrumentation.uninstrument_channel(channel=channel)
|
||||||
|
|
||||||
|
* PikaInstrumentor also supports instrumentation without creating an object, and receiving a tracer_provider
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
PikaInstrumentor.instrument_channel(channel, tracer_provider=tracer_provider)
|
||||||
|
|
||||||
|
API
|
||||||
|
---
|
||||||
|
"""
|
||||||
|
# pylint: disable=import-error
|
||||||
|
|
||||||
|
from .pika_instrumentor import PikaInstrumentor
|
||||||
|
from .version import __version__
|
||||||
|
|
||||||
|
__all__ = ["PikaInstrumentor", "__version__"]
|
@ -0,0 +1,16 @@
|
|||||||
|
# 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 Collection
|
||||||
|
|
||||||
|
_instruments: Collection[str] = ("pika >= 1.1.0",)
|
@ -0,0 +1,134 @@
|
|||||||
|
# Copyright The OpenTelemetry Authors
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
from logging import getLogger
|
||||||
|
from typing import Any, Callable, Collection, Dict, Optional
|
||||||
|
|
||||||
|
import wrapt
|
||||||
|
from pika.adapters import BlockingConnection
|
||||||
|
from pika.channel import Channel
|
||||||
|
|
||||||
|
from opentelemetry import trace
|
||||||
|
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
||||||
|
from opentelemetry.instrumentation.pika import utils
|
||||||
|
from opentelemetry.instrumentation.pika.package import _instruments
|
||||||
|
from opentelemetry.instrumentation.pika.version import __version__
|
||||||
|
from opentelemetry.instrumentation.utils import unwrap
|
||||||
|
from opentelemetry.trace import Tracer, TracerProvider
|
||||||
|
|
||||||
|
_LOG = getLogger(__name__)
|
||||||
|
_CTX_KEY = "__otel_task_span"
|
||||||
|
|
||||||
|
_FUNCTIONS_TO_UNINSTRUMENT = ["basic_publish"]
|
||||||
|
|
||||||
|
|
||||||
|
class PikaInstrumentor(BaseInstrumentor): # type: ignore
|
||||||
|
# pylint: disable=attribute-defined-outside-init
|
||||||
|
@staticmethod
|
||||||
|
def _instrument_consumers(
|
||||||
|
consumers_dict: Dict[str, Callable[..., Any]], tracer: Tracer
|
||||||
|
) -> Any:
|
||||||
|
for key, callback in consumers_dict.items():
|
||||||
|
decorated_callback = utils._decorate_callback(
|
||||||
|
callback, tracer, key
|
||||||
|
)
|
||||||
|
setattr(decorated_callback, "_original_callback", callback)
|
||||||
|
consumers_dict[key] = decorated_callback
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _instrument_basic_publish(channel: Channel, tracer: Tracer) -> None:
|
||||||
|
original_function = getattr(channel, "basic_publish")
|
||||||
|
decorated_function = utils._decorate_basic_publish(
|
||||||
|
original_function, channel, tracer
|
||||||
|
)
|
||||||
|
setattr(decorated_function, "_original_function", original_function)
|
||||||
|
channel.__setattr__("basic_publish", decorated_function)
|
||||||
|
channel.basic_publish = decorated_function
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _instrument_channel_functions(
|
||||||
|
channel: Channel, tracer: Tracer
|
||||||
|
) -> None:
|
||||||
|
if hasattr(channel, "basic_publish"):
|
||||||
|
PikaInstrumentor._instrument_basic_publish(channel, tracer)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _uninstrument_channel_functions(channel: Channel) -> None:
|
||||||
|
for function_name in _FUNCTIONS_TO_UNINSTRUMENT:
|
||||||
|
if not hasattr(channel, function_name):
|
||||||
|
continue
|
||||||
|
function = getattr(channel, function_name)
|
||||||
|
if hasattr(function, "_original_function"):
|
||||||
|
channel.__setattr__(function_name, function._original_function)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def instrument_channel(
|
||||||
|
channel: Channel, tracer_provider: Optional[TracerProvider] = None,
|
||||||
|
) -> None:
|
||||||
|
if not hasattr(channel, "_is_instrumented_by_opentelemetry"):
|
||||||
|
channel._is_instrumented_by_opentelemetry = False
|
||||||
|
if channel._is_instrumented_by_opentelemetry:
|
||||||
|
_LOG.warning(
|
||||||
|
"Attempting to instrument Pika channel while already instrumented!"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
|
||||||
|
if not hasattr(channel, "_impl"):
|
||||||
|
_LOG.error("Could not find implementation for provided channel!")
|
||||||
|
return
|
||||||
|
if channel._impl._consumers:
|
||||||
|
PikaInstrumentor._instrument_consumers(
|
||||||
|
channel._impl._consumers, tracer
|
||||||
|
)
|
||||||
|
PikaInstrumentor._instrument_channel_functions(channel, tracer)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def uninstrument_channel(channel: Channel) -> None:
|
||||||
|
if (
|
||||||
|
not hasattr(channel, "_is_instrumented_by_opentelemetry")
|
||||||
|
or not channel._is_instrumented_by_opentelemetry
|
||||||
|
):
|
||||||
|
_LOG.error(
|
||||||
|
"Attempting to uninstrument Pika channel while already uninstrumented!"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if not hasattr(channel, "_impl"):
|
||||||
|
_LOG.error("Could not find implementation for provided channel!")
|
||||||
|
return
|
||||||
|
for key, callback in channel._impl._consumers.items():
|
||||||
|
if hasattr(callback, "_original_callback"):
|
||||||
|
channel._impl._consumers[key] = callback._original_callback
|
||||||
|
PikaInstrumentor._uninstrument_channel_functions(channel)
|
||||||
|
|
||||||
|
def _decorate_channel_function(
|
||||||
|
self, tracer_provider: Optional[TracerProvider]
|
||||||
|
) -> None:
|
||||||
|
def wrapper(wrapped, instance, args, kwargs):
|
||||||
|
channel = wrapped(*args, **kwargs)
|
||||||
|
self.instrument_channel(channel, tracer_provider=tracer_provider)
|
||||||
|
return channel
|
||||||
|
|
||||||
|
wrapt.wrap_function_wrapper(BlockingConnection, "channel", wrapper)
|
||||||
|
|
||||||
|
def _instrument(self, **kwargs: Dict[str, Any]) -> None:
|
||||||
|
tracer_provider: TracerProvider = kwargs.get("tracer_provider", None)
|
||||||
|
self.__setattr__("__opentelemetry_tracer_provider", tracer_provider)
|
||||||
|
self._decorate_channel_function(tracer_provider)
|
||||||
|
|
||||||
|
def _uninstrument(self, **kwargs: Dict[str, Any]) -> None:
|
||||||
|
if hasattr(self, "__opentelemetry_tracer_provider"):
|
||||||
|
delattr(self, "__opentelemetry_tracer_provider")
|
||||||
|
unwrap(BlockingConnection, "channel")
|
||||||
|
|
||||||
|
def instrumentation_dependencies(self) -> Collection[str]:
|
||||||
|
return _instruments
|
@ -0,0 +1,160 @@
|
|||||||
|
from typing import Any, Callable, List, Optional
|
||||||
|
|
||||||
|
from pika.channel import Channel
|
||||||
|
from pika.spec import Basic, BasicProperties
|
||||||
|
|
||||||
|
from opentelemetry import context, propagate, trace
|
||||||
|
from opentelemetry.instrumentation.utils import _SUPPRESS_INSTRUMENTATION_KEY
|
||||||
|
from opentelemetry.propagators.textmap import CarrierT, Getter
|
||||||
|
from opentelemetry.semconv.trace import (
|
||||||
|
MessagingOperationValues,
|
||||||
|
SpanAttributes,
|
||||||
|
)
|
||||||
|
from opentelemetry.trace import Tracer
|
||||||
|
from opentelemetry.trace.span import Span
|
||||||
|
|
||||||
|
|
||||||
|
class _PikaGetter(Getter): # type: ignore
|
||||||
|
def get(self, carrier: CarrierT, key: str) -> Optional[List[str]]:
|
||||||
|
value = carrier.get(key, None)
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
return [value]
|
||||||
|
|
||||||
|
def keys(self, carrier: CarrierT) -> List[str]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
_pika_getter = _PikaGetter()
|
||||||
|
|
||||||
|
|
||||||
|
def _decorate_callback(
|
||||||
|
callback: Callable[[Channel, Basic.Deliver, BasicProperties, bytes], Any],
|
||||||
|
tracer: Tracer,
|
||||||
|
task_name: str,
|
||||||
|
):
|
||||||
|
def decorated_callback(
|
||||||
|
channel: Channel,
|
||||||
|
method: Basic.Deliver,
|
||||||
|
properties: BasicProperties,
|
||||||
|
body: bytes,
|
||||||
|
) -> Any:
|
||||||
|
if not properties:
|
||||||
|
properties = BasicProperties()
|
||||||
|
span = _get_span(
|
||||||
|
tracer,
|
||||||
|
channel,
|
||||||
|
properties,
|
||||||
|
task_name=task_name,
|
||||||
|
operation=MessagingOperationValues.RECEIVE,
|
||||||
|
)
|
||||||
|
with trace.use_span(span, end_on_exit=True):
|
||||||
|
propagate.inject(properties.headers)
|
||||||
|
retval = callback(channel, method, properties, body)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
return decorated_callback
|
||||||
|
|
||||||
|
|
||||||
|
def _decorate_basic_publish(
|
||||||
|
original_function: Callable[[str, str, bytes, BasicProperties, bool], Any],
|
||||||
|
channel: Channel,
|
||||||
|
tracer: Tracer,
|
||||||
|
):
|
||||||
|
def decorated_function(
|
||||||
|
exchange: str,
|
||||||
|
routing_key: str,
|
||||||
|
body: bytes,
|
||||||
|
properties: BasicProperties = None,
|
||||||
|
mandatory: bool = False,
|
||||||
|
) -> Any:
|
||||||
|
if not properties:
|
||||||
|
properties = BasicProperties()
|
||||||
|
span = _get_span(
|
||||||
|
tracer,
|
||||||
|
channel,
|
||||||
|
properties,
|
||||||
|
task_name="(temporary)",
|
||||||
|
operation=None,
|
||||||
|
)
|
||||||
|
if not span:
|
||||||
|
return original_function(
|
||||||
|
exchange, routing_key, body, properties, mandatory
|
||||||
|
)
|
||||||
|
with trace.use_span(span, end_on_exit=True):
|
||||||
|
if span.is_recording():
|
||||||
|
propagate.inject(properties.headers)
|
||||||
|
retval = original_function(
|
||||||
|
exchange, routing_key, body, properties, mandatory
|
||||||
|
)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
return decorated_function
|
||||||
|
|
||||||
|
|
||||||
|
def _get_span(
|
||||||
|
tracer: Tracer,
|
||||||
|
channel: Channel,
|
||||||
|
properties: BasicProperties,
|
||||||
|
task_name: str,
|
||||||
|
operation: Optional[MessagingOperationValues] = None,
|
||||||
|
) -> Optional[Span]:
|
||||||
|
if properties.headers is None:
|
||||||
|
properties.headers = {}
|
||||||
|
ctx = propagate.extract(properties.headers, getter=_pika_getter)
|
||||||
|
if context.get_value("suppress_instrumentation") or context.get_value(
|
||||||
|
_SUPPRESS_INSTRUMENTATION_KEY
|
||||||
|
):
|
||||||
|
return None
|
||||||
|
task_name = properties.type if properties.type else task_name
|
||||||
|
span = tracer.start_span(
|
||||||
|
context=ctx, name=_generate_span_name(task_name, operation)
|
||||||
|
)
|
||||||
|
if span.is_recording():
|
||||||
|
_enrich_span(span, channel, properties, task_name, operation)
|
||||||
|
return span
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_span_name(
|
||||||
|
task_name: str, operation: Optional[MessagingOperationValues]
|
||||||
|
) -> str:
|
||||||
|
if not operation:
|
||||||
|
return f"{task_name} send"
|
||||||
|
return f"{task_name} {operation.value}"
|
||||||
|
|
||||||
|
|
||||||
|
def _enrich_span(
|
||||||
|
span: Span,
|
||||||
|
channel: Channel,
|
||||||
|
properties: BasicProperties,
|
||||||
|
task_destination: str,
|
||||||
|
operation: Optional[MessagingOperationValues] = None,
|
||||||
|
) -> None:
|
||||||
|
span.set_attribute(SpanAttributes.MESSAGING_SYSTEM, "rabbitmq")
|
||||||
|
if operation:
|
||||||
|
span.set_attribute(SpanAttributes.MESSAGING_OPERATION, operation.value)
|
||||||
|
else:
|
||||||
|
span.set_attribute(SpanAttributes.MESSAGING_TEMP_DESTINATION, True)
|
||||||
|
span.set_attribute(SpanAttributes.MESSAGING_DESTINATION, task_destination)
|
||||||
|
if properties.message_id:
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.MESSAGING_MESSAGE_ID, properties.message_id
|
||||||
|
)
|
||||||
|
if properties.correlation_id:
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.MESSAGING_CONVERSATION_ID, properties.correlation_id
|
||||||
|
)
|
||||||
|
if not hasattr(channel.connection, "params"):
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.NET_PEER_NAME, channel.connection._impl.params.host
|
||||||
|
)
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.NET_PEER_PORT, channel.connection._impl.params.port
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.NET_PEER_NAME, channel.connection.params.host
|
||||||
|
)
|
||||||
|
span.set_attribute(
|
||||||
|
SpanAttributes.NET_PEER_PORT, channel.connection.params.port
|
||||||
|
)
|
@ -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.24b0"
|
@ -0,0 +1,37 @@
|
|||||||
|
# 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 unittest import TestCase
|
||||||
|
|
||||||
|
from opentelemetry.instrumentation.pika.utils import _PikaGetter
|
||||||
|
|
||||||
|
|
||||||
|
class TestPikaGetter(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.getter = _PikaGetter()
|
||||||
|
|
||||||
|
def test_get_none(self) -> None:
|
||||||
|
carrier = {}
|
||||||
|
value = self.getter.get(carrier, "test")
|
||||||
|
self.assertIsNone(value)
|
||||||
|
|
||||||
|
def test_get_value(self) -> None:
|
||||||
|
key = "test"
|
||||||
|
value = "value"
|
||||||
|
carrier = {key: value}
|
||||||
|
val = self.getter.get(carrier, key)
|
||||||
|
self.assertEqual(val, [value])
|
||||||
|
|
||||||
|
def test_keys(self):
|
||||||
|
keys = self.getter.keys({})
|
||||||
|
self.assertEqual(keys, [])
|
@ -0,0 +1,104 @@
|
|||||||
|
# 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 unittest import TestCase, mock
|
||||||
|
|
||||||
|
from pika.adapters import BaseConnection, BlockingConnection
|
||||||
|
from pika.channel import Channel
|
||||||
|
from wrapt import BoundFunctionWrapper
|
||||||
|
|
||||||
|
from opentelemetry.instrumentation.pika import PikaInstrumentor
|
||||||
|
from opentelemetry.trace import Tracer
|
||||||
|
|
||||||
|
|
||||||
|
class TestPika(TestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
self.channel = mock.MagicMock(spec=Channel)
|
||||||
|
self.channel._impl = mock.MagicMock(spec=BaseConnection)
|
||||||
|
self.mock_callback = mock.MagicMock()
|
||||||
|
self.channel._impl._consumers = {"mock_key": self.mock_callback}
|
||||||
|
|
||||||
|
def test_instrument_api(self) -> None:
|
||||||
|
instrumentation = PikaInstrumentor()
|
||||||
|
instrumentation.instrument()
|
||||||
|
self.assertTrue(
|
||||||
|
isinstance(BlockingConnection.channel, BoundFunctionWrapper)
|
||||||
|
)
|
||||||
|
assert hasattr(
|
||||||
|
instrumentation, "__opentelemetry_tracer_provider"
|
||||||
|
), "Tracer not stored for the object!"
|
||||||
|
instrumentation.uninstrument(channel=self.channel)
|
||||||
|
self.assertFalse(
|
||||||
|
isinstance(BlockingConnection.channel, BoundFunctionWrapper)
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch(
|
||||||
|
"opentelemetry.instrumentation.pika.PikaInstrumentor._instrument_channel_functions"
|
||||||
|
)
|
||||||
|
@mock.patch(
|
||||||
|
"opentelemetry.instrumentation.pika.PikaInstrumentor._instrument_consumers"
|
||||||
|
)
|
||||||
|
def test_instrument(
|
||||||
|
self,
|
||||||
|
instrument_consumers: mock.MagicMock,
|
||||||
|
instrument_channel_functions: mock.MagicMock,
|
||||||
|
):
|
||||||
|
PikaInstrumentor.instrument_channel(channel=self.channel)
|
||||||
|
assert hasattr(
|
||||||
|
self.channel, "_is_instrumented_by_opentelemetry"
|
||||||
|
), "channel is not marked as instrumented!"
|
||||||
|
instrument_consumers.assert_called_once()
|
||||||
|
instrument_channel_functions.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch("opentelemetry.instrumentation.pika.utils._decorate_callback")
|
||||||
|
def test_instrument_consumers(
|
||||||
|
self, decorate_callback: mock.MagicMock
|
||||||
|
) -> None:
|
||||||
|
tracer = mock.MagicMock(spec=Tracer)
|
||||||
|
expected_decoration_calls = [
|
||||||
|
mock.call(value, tracer, key)
|
||||||
|
for key, value in self.channel._impl._consumers.items()
|
||||||
|
]
|
||||||
|
PikaInstrumentor._instrument_consumers(
|
||||||
|
self.channel._impl._consumers, tracer
|
||||||
|
)
|
||||||
|
decorate_callback.assert_has_calls(
|
||||||
|
calls=expected_decoration_calls, any_order=True
|
||||||
|
)
|
||||||
|
assert all(
|
||||||
|
hasattr(callback, "_original_callback")
|
||||||
|
for callback in self.channel._impl._consumers.values()
|
||||||
|
)
|
||||||
|
|
||||||
|
@mock.patch(
|
||||||
|
"opentelemetry.instrumentation.pika.utils._decorate_basic_publish"
|
||||||
|
)
|
||||||
|
def test_instrument_basic_publish(
|
||||||
|
self, decorate_basic_publish: mock.MagicMock
|
||||||
|
) -> None:
|
||||||
|
tracer = mock.MagicMock(spec=Tracer)
|
||||||
|
original_function = self.channel.basic_publish
|
||||||
|
PikaInstrumentor._instrument_basic_publish(self.channel, tracer)
|
||||||
|
decorate_basic_publish.assert_called_once_with(
|
||||||
|
original_function, self.channel, tracer
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
self.channel.basic_publish, decorate_basic_publish.return_value
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_uninstrument_channel_functions(self) -> None:
|
||||||
|
original_function = self.channel.basic_publish
|
||||||
|
self.channel.basic_publish = mock.MagicMock()
|
||||||
|
self.channel.basic_publish._original_function = original_function
|
||||||
|
PikaInstrumentor._uninstrument_channel_functions(self.channel)
|
||||||
|
self.assertEqual(self.channel.basic_publish, original_function)
|
@ -0,0 +1,163 @@
|
|||||||
|
# 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 unittest import TestCase, mock
|
||||||
|
|
||||||
|
from opentelemetry.instrumentation.pika import utils
|
||||||
|
from opentelemetry.semconv.trace import SpanAttributes
|
||||||
|
from opentelemetry.trace import Span, Tracer
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtils(TestCase):
|
||||||
|
@staticmethod
|
||||||
|
@mock.patch("opentelemetry.context.get_value")
|
||||||
|
@mock.patch("opentelemetry.instrumentation.pika.utils._generate_span_name")
|
||||||
|
@mock.patch("opentelemetry.instrumentation.pika.utils._enrich_span")
|
||||||
|
@mock.patch("opentelemetry.propagate.extract")
|
||||||
|
def test_get_span(
|
||||||
|
extract: mock.MagicMock,
|
||||||
|
enrich_span: mock.MagicMock,
|
||||||
|
generate_span_name: mock.MagicMock,
|
||||||
|
get_value: mock.MagicMock,
|
||||||
|
) -> None:
|
||||||
|
tracer = mock.MagicMock(spec=Tracer)
|
||||||
|
channel = mock.MagicMock()
|
||||||
|
properties = mock.MagicMock()
|
||||||
|
task_name = "test.test"
|
||||||
|
get_value.return_value = None
|
||||||
|
_ = utils._get_span(tracer, channel, properties, task_name)
|
||||||
|
extract.assert_called_once()
|
||||||
|
generate_span_name.assert_called_once()
|
||||||
|
tracer.start_span.assert_called_once_with(
|
||||||
|
context=extract.return_value, name=generate_span_name.return_value
|
||||||
|
)
|
||||||
|
enrich_span.assert_called_once()
|
||||||
|
|
||||||
|
@mock.patch("opentelemetry.context.get_value")
|
||||||
|
@mock.patch("opentelemetry.instrumentation.pika.utils._generate_span_name")
|
||||||
|
@mock.patch("opentelemetry.instrumentation.pika.utils._enrich_span")
|
||||||
|
@mock.patch("opentelemetry.propagate.extract")
|
||||||
|
def test_get_span_suppressed(
|
||||||
|
self,
|
||||||
|
extract: mock.MagicMock,
|
||||||
|
enrich_span: mock.MagicMock,
|
||||||
|
generate_span_name: mock.MagicMock,
|
||||||
|
get_value: mock.MagicMock,
|
||||||
|
) -> None:
|
||||||
|
tracer = mock.MagicMock(spec=Tracer)
|
||||||
|
channel = mock.MagicMock()
|
||||||
|
properties = mock.MagicMock()
|
||||||
|
task_name = "test.test"
|
||||||
|
get_value.return_value = True
|
||||||
|
span = utils._get_span(tracer, channel, properties, task_name)
|
||||||
|
self.assertEqual(span, None)
|
||||||
|
extract.assert_called_once()
|
||||||
|
generate_span_name.assert_not_called()
|
||||||
|
|
||||||
|
def test_generate_span_name_no_operation(self) -> None:
|
||||||
|
task_name = "test.test"
|
||||||
|
operation = None
|
||||||
|
span_name = utils._generate_span_name(task_name, operation)
|
||||||
|
self.assertEqual(span_name, f"{task_name} send")
|
||||||
|
|
||||||
|
def test_generate_span_name_with_operation(self) -> None:
|
||||||
|
task_name = "test.test"
|
||||||
|
operation = mock.MagicMock()
|
||||||
|
operation.value = "process"
|
||||||
|
span_name = utils._generate_span_name(task_name, operation)
|
||||||
|
self.assertEqual(span_name, f"{task_name} {operation.value}")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def test_enrich_span_basic_values() -> None:
|
||||||
|
channel = mock.MagicMock()
|
||||||
|
properties = mock.MagicMock()
|
||||||
|
task_destination = "test.test"
|
||||||
|
span = mock.MagicMock(spec=Span)
|
||||||
|
utils._enrich_span(span, channel, properties, task_destination)
|
||||||
|
span.set_attribute.assert_has_calls(
|
||||||
|
any_order=True,
|
||||||
|
calls=[
|
||||||
|
mock.call(SpanAttributes.MESSAGING_SYSTEM, "rabbitmq"),
|
||||||
|
mock.call(SpanAttributes.MESSAGING_TEMP_DESTINATION, True),
|
||||||
|
mock.call(
|
||||||
|
SpanAttributes.MESSAGING_DESTINATION, task_destination
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
SpanAttributes.MESSAGING_MESSAGE_ID, properties.message_id
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
SpanAttributes.MESSAGING_CONVERSATION_ID,
|
||||||
|
properties.correlation_id,
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
SpanAttributes.NET_PEER_NAME,
|
||||||
|
channel.connection.params.host,
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
SpanAttributes.NET_PEER_PORT,
|
||||||
|
channel.connection.params.port,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def test_enrich_span_with_operation() -> None:
|
||||||
|
channel = mock.MagicMock()
|
||||||
|
properties = mock.MagicMock()
|
||||||
|
task_destination = "test.test"
|
||||||
|
operation = mock.MagicMock()
|
||||||
|
span = mock.MagicMock(spec=Span)
|
||||||
|
utils._enrich_span(
|
||||||
|
span, channel, properties, task_destination, operation
|
||||||
|
)
|
||||||
|
span.set_attribute.assert_has_calls(
|
||||||
|
any_order=True,
|
||||||
|
calls=[
|
||||||
|
mock.call(SpanAttributes.MESSAGING_OPERATION, operation.value)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def test_enrich_span_without_operation() -> None:
|
||||||
|
channel = mock.MagicMock()
|
||||||
|
properties = mock.MagicMock()
|
||||||
|
task_destination = "test.test"
|
||||||
|
span = mock.MagicMock(spec=Span)
|
||||||
|
utils._enrich_span(span, channel, properties, task_destination)
|
||||||
|
span.set_attribute.assert_has_calls(
|
||||||
|
any_order=True,
|
||||||
|
calls=[mock.call(SpanAttributes.MESSAGING_TEMP_DESTINATION, True)],
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def test_enrich_span_unique_connection() -> None:
|
||||||
|
channel = mock.MagicMock()
|
||||||
|
properties = mock.MagicMock()
|
||||||
|
task_destination = "test.test"
|
||||||
|
span = mock.MagicMock(spec=Span)
|
||||||
|
# We do this to create the behaviour of hasattr(channel.connection, "params") == False
|
||||||
|
del channel.connection.params
|
||||||
|
utils._enrich_span(span, channel, properties, task_destination)
|
||||||
|
span.set_attribute.assert_has_calls(
|
||||||
|
any_order=True,
|
||||||
|
calls=[
|
||||||
|
mock.call(
|
||||||
|
SpanAttributes.NET_PEER_NAME,
|
||||||
|
channel.connection._impl.params.host,
|
||||||
|
),
|
||||||
|
mock.call(
|
||||||
|
SpanAttributes.NET_PEER_PORT,
|
||||||
|
channel.connection._impl.params.port,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
9
tox.ini
9
tox.ini
@ -153,6 +153,10 @@ envlist =
|
|||||||
py3{6,7,8,9}-test-propagator-ot-trace
|
py3{6,7,8,9}-test-propagator-ot-trace
|
||||||
pypy3-test-propagator-ot-trace
|
pypy3-test-propagator-ot-trace
|
||||||
|
|
||||||
|
; opentelemetry-instrumentation-pika
|
||||||
|
py3{6,7,8,9}-test-instrumentation-pika
|
||||||
|
pypy3-test-instrumentation-pika
|
||||||
|
|
||||||
lint
|
lint
|
||||||
docker-tests
|
docker-tests
|
||||||
docs
|
docs
|
||||||
@ -210,6 +214,7 @@ changedir =
|
|||||||
test-instrumentation-jinja2: instrumentation/opentelemetry-instrumentation-jinja2/tests
|
test-instrumentation-jinja2: instrumentation/opentelemetry-instrumentation-jinja2/tests
|
||||||
test-instrumentation-logging: instrumentation/opentelemetry-instrumentation-logging/tests
|
test-instrumentation-logging: instrumentation/opentelemetry-instrumentation-logging/tests
|
||||||
test-instrumentation-mysql: instrumentation/opentelemetry-instrumentation-mysql/tests
|
test-instrumentation-mysql: instrumentation/opentelemetry-instrumentation-mysql/tests
|
||||||
|
test-instrumentation-pika: instrumentation/opentelemetry-instrumentation-pika/tests
|
||||||
test-instrumentation-psycopg2: instrumentation/opentelemetry-instrumentation-psycopg2/tests
|
test-instrumentation-psycopg2: instrumentation/opentelemetry-instrumentation-psycopg2/tests
|
||||||
test-instrumentation-pymemcache: instrumentation/opentelemetry-instrumentation-pymemcache/tests
|
test-instrumentation-pymemcache: instrumentation/opentelemetry-instrumentation-pymemcache/tests
|
||||||
test-instrumentation-pymongo: instrumentation/opentelemetry-instrumentation-pymongo/tests
|
test-instrumentation-pymongo: instrumentation/opentelemetry-instrumentation-pymongo/tests
|
||||||
@ -242,6 +247,8 @@ commands_pre =
|
|||||||
|
|
||||||
celery: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test]
|
celery: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test]
|
||||||
|
|
||||||
|
pika: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-pika[test]
|
||||||
|
|
||||||
grpc: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc[test]
|
grpc: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-grpc[test]
|
||||||
|
|
||||||
falcon{2,3},flask,django,pyramid,tornado,starlette,fastapi,aiohttp,asgi,requests,urllib,urllib3,wsgi: pip install {toxinidir}/util/opentelemetry-util-http[test]
|
falcon{2,3},flask,django,pyramid,tornado,starlette,fastapi,aiohttp,asgi,requests,urllib,urllib3,wsgi: pip install {toxinidir}/util/opentelemetry-util-http[test]
|
||||||
@ -372,6 +379,7 @@ commands_pre =
|
|||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-flask[test]
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-flask[test]
|
||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy[test]
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-sqlalchemy[test]
|
||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test]
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-celery[test]
|
||||||
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-pika[test]
|
||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn[test]
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-sklearn[test]
|
||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-redis[test]
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-redis[test]
|
||||||
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi[test]
|
python -m pip install -e {toxinidir}/instrumentation/opentelemetry-instrumentation-fastapi[test]
|
||||||
@ -430,6 +438,7 @@ commands_pre =
|
|||||||
"{env:CORE_REPO}#egg=opentelemetry-test&subdirectory=tests/util" \
|
"{env:CORE_REPO}#egg=opentelemetry-test&subdirectory=tests/util" \
|
||||||
-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-pika \
|
||||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi \
|
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-dbapi \
|
||||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql \
|
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-mysql \
|
||||||
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2 \
|
-e {toxinidir}/instrumentation/opentelemetry-instrumentation-psycopg2 \
|
||||||
|
Reference in New Issue
Block a user