Skip to content

Commit 9ccb612

Browse files
committed
Added aio-pika instrumentation
1 parent 80969a0 commit 9ccb612

18 files changed

+790
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
OpenTelemetry aio_pika Instrumentation
2+
==================================
3+
4+
|pypi|
5+
6+
.. |pypi| image:: https://badge.fury.io/py/opentelemetry-instrumentation-aio-pika.svg
7+
:target: https://pypi.org/project/opentelemetry-instrumentation-aio-pika/
8+
9+
This library allows tracing requests made by the aio_pika library.
10+
11+
Installation
12+
------------
13+
14+
::
15+
16+
pip install opentelemetry-instrumentation-aio-pika
17+
18+
References
19+
----------
20+
21+
* `OpenTelemetry aio_pika/ Tracing <https://opentelemetry-python-contrib.readthedocs.io/en/latest/instrumentation/aio-pika/aio-pika.html>`_
22+
* `OpenTelemetry Project <https://opentelemetry.io/>`_
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
[metadata]
16+
name = opentelemetry-instrumentation-aio-pika
17+
description = OpenTelemetry aio_pika instrumentation
18+
long_description = file: README.rst
19+
long_description_content_type = text/x-rst
20+
author = OpenTelemetry Authors
21+
author_email = [email protected]
22+
url = https://github.com/open-telemetry/opentelemetry-python-contrib/instrumentation/opentelemetry-instrumentation-aio-pika
23+
platforms = any
24+
license = Apache-2.0
25+
classifiers =
26+
Development Status :: 4 - Beta
27+
Intended Audience :: Developers
28+
License :: OSI Approved :: Apache Software License
29+
Programming Language :: Python
30+
Programming Language :: Python :: 3
31+
Programming Language :: Python :: 3.6
32+
Programming Language :: Python :: 3.7
33+
Programming Language :: Python :: 3.8
34+
Programming Language :: Python :: 3.9
35+
Programming Language :: Python :: 3.10
36+
37+
[options]
38+
python_requires = >=3.6
39+
package_dir=
40+
=src
41+
packages=find_namespace:
42+
43+
install_requires =
44+
opentelemetry-api ~= 1.5
45+
wrapt >= 1.0.0, < 2.0.0
46+
47+
[options.extras_require]
48+
test =
49+
pytest
50+
wrapt >= 1.0.0, < 2.0.0
51+
opentelemetry-test-utils == 0.30b1
52+
53+
[options.packages.find]
54+
where = src
55+
56+
[options.entry_points]
57+
opentelemetry_instrumentor =
58+
aio-pika = opentelemetry.instrumentation.aio_pika:AioPikaInstrumentor
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
16+
# DO NOT EDIT. THIS FILE WAS AUTOGENERATED FROM templates/instrumentation_setup.py.txt.
17+
# RUN `python scripts/generate_setup.py` TO REGENERATE.
18+
19+
20+
import distutils.cmd
21+
import json
22+
import os
23+
from configparser import ConfigParser
24+
25+
import setuptools
26+
27+
config = ConfigParser()
28+
config.read("setup.cfg")
29+
30+
# We provide extras_require parameter to setuptools.setup later which
31+
# overwrites the extras_require section from setup.cfg. To support extras_require
32+
# section in setup.cfg, we load it here and merge it with the extras_require param.
33+
extras_require = {}
34+
if "options.extras_require" in config:
35+
for key, value in config["options.extras_require"].items():
36+
extras_require[key] = [v for v in value.split("\n") if v.strip()]
37+
38+
BASE_DIR = os.path.dirname(__file__)
39+
PACKAGE_INFO = {}
40+
41+
VERSION_FILENAME = os.path.join(
42+
BASE_DIR, "src", "opentelemetry", "instrumentation", "aio_pika", "version.py"
43+
)
44+
with open(VERSION_FILENAME, encoding="utf-8") as f:
45+
exec(f.read(), PACKAGE_INFO)
46+
47+
PACKAGE_FILENAME = os.path.join(
48+
BASE_DIR, "src", "opentelemetry", "instrumentation", "aio_pika", "package.py"
49+
)
50+
with open(PACKAGE_FILENAME, encoding="utf-8") as f:
51+
exec(f.read(), PACKAGE_INFO)
52+
53+
# Mark any instruments/runtime dependencies as test dependencies as well.
54+
extras_require["instruments"] = PACKAGE_INFO["_instruments"]
55+
test_deps = extras_require.get("test", [])
56+
for dep in extras_require["instruments"]:
57+
test_deps.append(dep)
58+
59+
extras_require["test"] = test_deps
60+
61+
62+
class JSONMetadataCommand(distutils.cmd.Command):
63+
64+
description = (
65+
"print out package metadata as JSON. This is used by OpenTelemetry dev scripts to ",
66+
"auto-generate code in other places",
67+
)
68+
user_options = []
69+
70+
def initialize_options(self):
71+
pass
72+
73+
def finalize_options(self):
74+
pass
75+
76+
def run(self):
77+
metadata = {
78+
"name": config["metadata"]["name"],
79+
"version": PACKAGE_INFO["__version__"],
80+
"instruments": PACKAGE_INFO["_instruments"],
81+
}
82+
print(json.dumps(metadata))
83+
84+
85+
setuptools.setup(
86+
cmdclass={"meta": JSONMetadataCommand},
87+
version=PACKAGE_INFO["__version__"],
88+
extras_require=extras_require,
89+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
Instrument `aio_pika` to trace RabbitMQ applications.
16+
17+
Usage
18+
-----
19+
20+
* Start broker backend
21+
22+
.. code-block:: python
23+
24+
docker run -p 5672:5672 rabbitmq
25+
26+
* Run instrumented task
27+
28+
.. code-block:: python
29+
30+
import asyncio
31+
32+
from aio_pika import Message, connect
33+
from opentelemetry.instrumentation.aio_pika import AioPikaInstrumentor
34+
35+
AioPikaInstrumentor().instrument()
36+
37+
38+
async def main() -> None:
39+
connection = await connect("amqp://guest:guest@localhost/")
40+
async with connection:
41+
channel = await connection.channel()
42+
queue = await channel.declare_queue("hello")
43+
await channel.default_exchange.publish(
44+
Message(b"Hello World!"),
45+
routing_key=queue.name,
46+
)
47+
48+
49+
if __name__ == "__main__":
50+
asyncio.run(main())
51+
52+
API
53+
---
54+
"""
55+
# pylint: disable=import-error
56+
57+
from .aio_pika_instrumentor import AioPikaInstrumentor
58+
from .version import __version__
59+
60+
__all__ = ["AioPikaInstrumentor", "__version__"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from typing import Optional, List
15+
from opentelemetry.propagators.textmap import Getter, CarrierT
16+
17+
18+
class _AioPikaGetter(Getter): # type: ignore
19+
def get(self, carrier: CarrierT, key: str) -> Optional[List[str]]:
20+
value = carrier.get(key, None)
21+
if value is None:
22+
return None
23+
return [value]
24+
25+
def keys(self, carrier: CarrierT) -> List[str]:
26+
return []
27+
28+
29+
aio_pika_getter = _AioPikaGetter()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from typing import Collection
15+
16+
import aio_pika
17+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
18+
19+
from .package import _instruments
20+
from .span_builder import SpanBuilder
21+
from .instrumented_exchange import InstrumentedExchange, RobustInstrumentedExchange
22+
from .instrumented_queue import InstrumentedQueue, RobustInstrumentedQueue
23+
24+
25+
class AioPikaInstrumentor(BaseInstrumentor):
26+
27+
def _instrument(self, **kwargs):
28+
tracer_provider = kwargs.get('tracer_provider', None)
29+
SpanBuilder.TRACER_PROVIDER = tracer_provider
30+
aio_pika.Channel.EXCHANGE_CLASS = InstrumentedExchange
31+
aio_pika.Channel.QUEUE_CLASS = InstrumentedQueue
32+
aio_pika.RobustChannel.EXCHANGE_CLASS = RobustInstrumentedExchange
33+
aio_pika.RobustChannel.QUEUE_CLASS = RobustInstrumentedQueue
34+
35+
def _uninstrument(self, **kwargs):
36+
SpanBuilder.TRACER_PROVIDER = None
37+
aio_pika.Channel.EXCHANGE_CLASS = aio_pika.Exchange
38+
aio_pika.Channel.QUEUE_CLASS = aio_pika.Queue
39+
aio_pika.RobustChannel.EXCHANGE_CLASS = aio_pika.RobustExchange
40+
aio_pika.RobustChannel.QUEUE_CLASS = aio_pika.RobustQueue
41+
42+
def instrumentation_dependencies(self) -> Collection[str]:
43+
return _instruments
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
from typing import Optional
15+
16+
import aiormq
17+
from aio_pika import Exchange, RobustExchange
18+
from aio_pika.abc import AbstractMessage
19+
from opentelemetry import trace, propagate
20+
from opentelemetry.trace import Span
21+
22+
from opentelemetry.instrumentation.aio_pika.span_builder import SpanBuilder
23+
24+
25+
class InstrumentedExchange(Exchange):
26+
def _get_publish_span(self, message: AbstractMessage, routing_key: str) -> Optional[Span]:
27+
builder = SpanBuilder()
28+
builder.set_as_producer()
29+
builder.set_destination(f'{self.name},{routing_key}')
30+
builder.set_channel(self.channel)
31+
builder.set_message(message)
32+
return builder.build()
33+
34+
async def publish(self, message: AbstractMessage, routing_key: str, **kwargs) -> Optional[aiormq.abc.ConfirmationFrameType]:
35+
span = self._get_publish_span(message, routing_key)
36+
if not span:
37+
return await super().publish(message, routing_key, **kwargs)
38+
with trace.use_span(span, end_on_exit=True):
39+
if span.is_recording():
40+
propagate.inject(message.properties.headers)
41+
return_value = await super().publish(message, routing_key, **kwargs)
42+
return return_value
43+
44+
45+
class RobustInstrumentedExchange(RobustExchange, InstrumentedExchange):
46+
pass

0 commit comments

Comments
 (0)