Skip to content

Commit fa11167

Browse files
Support aio_pika 8
- Fix tests for new shape of the AbstractConnection class - Run tests against aio_pika 7 and 8
1 parent c92ba14 commit fa11167

File tree

11 files changed

+156
-31
lines changed

11 files changed

+156
-31
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
([#1553](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1553))
1414
- `opentelemetry/sdk/extension/aws` Implement [`aws.ecs.*`](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud_provider/aws/ecs.md) and [`aws.logs.*`](https://opentelemetry.io/docs/reference/specification/resource/semantic_conventions/cloud_provider/aws/logs/) resource attributes in the `AwsEcsResourceDetector` detector when the ECS Metadata v4 is available
1515
([#1212](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1212))
16+
- `opentelemetry-instrumentation-aio-pika` Support `aio_pika` 8.x
17+
([#1481](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1481))
1618

1719
### Fixed
1820

instrumentation/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
| Instrumentation | Supported Packages | Metrics support |
33
| --------------- | ------------------ | --------------- |
4-
| [opentelemetry-instrumentation-aio-pika](./opentelemetry-instrumentation-aio-pika) | aio_pika ~= 7.2.0 | No
4+
| [opentelemetry-instrumentation-aio-pika](./opentelemetry-instrumentation-aio-pika) | aio_pika >= 7.2.0, < 9.0.0 | No
55
| [opentelemetry-instrumentation-aiohttp-client](./opentelemetry-instrumentation-aiohttp-client) | aiohttp ~= 3.0 | No
66
| [opentelemetry-instrumentation-aiopg](./opentelemetry-instrumentation-aiopg) | aiopg >= 0.13.0, < 2.0.0 | No
77
| [opentelemetry-instrumentation-asgi](./opentelemetry-instrumentation-asgi) | asgiref ~= 3.0 | No

instrumentation/opentelemetry-instrumentation-aio-pika/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ dependencies = [
3131

3232
[project.optional-dependencies]
3333
instruments = [
34-
"aio_pika ~= 7.2.0",
34+
"aio_pika >= 7.2.0, < 9.0.0",
3535
]
3636
test = [
3737
"opentelemetry-instrumentation-aio-pika[instruments]",

instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/package.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@
1313
# limitations under the License.
1414
from typing import Collection
1515

16-
_instruments: Collection[str] = ("aio_pika ~= 7.2.0",)
16+
_instruments: Collection[str] = ("aio_pika >= 7.2.0, < 9.0.0",)

instrumentation/opentelemetry-instrumentation-aio-pika/src/opentelemetry/instrumentation/aio_pika/span_builder.py

+25-11
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
)
2525
from opentelemetry.trace import Span, SpanKind, Tracer
2626

27-
_DEFAULT_ATTRIBUTES = {SpanAttributes.MESSAGING_SYSTEM: 'rabbitmq'}
27+
_DEFAULT_ATTRIBUTES = {SpanAttributes.MESSAGING_SYSTEM: "rabbitmq"}
2828

2929

3030
class SpanBuilder:
@@ -49,18 +49,30 @@ def set_destination(self, destination: str):
4949
self._attributes[SpanAttributes.MESSAGING_DESTINATION] = destination
5050

5151
def set_channel(self, channel: AbstractChannel):
52-
url = channel.connection.connection.url
53-
self._attributes.update({
54-
SpanAttributes.NET_PEER_NAME: url.host,
55-
SpanAttributes.NET_PEER_PORT: url.port
56-
})
52+
connection = channel.connection
53+
if getattr(connection, "connection", None):
54+
# aio_rmq 7
55+
url = connection.connection.url
56+
else:
57+
# aio_rmq 8
58+
url = connection.url
59+
self._attributes.update(
60+
{
61+
SpanAttributes.NET_PEER_NAME: url.host,
62+
SpanAttributes.NET_PEER_PORT: url.port,
63+
}
64+
)
5765

5866
def set_message(self, message: AbstractMessage):
5967
properties = message.properties
6068
if properties.message_id:
61-
self._attributes[SpanAttributes.MESSAGING_MESSAGE_ID] = properties.message_id
69+
self._attributes[
70+
SpanAttributes.MESSAGING_MESSAGE_ID
71+
] = properties.message_id
6272
if properties.correlation_id:
63-
self._attributes[SpanAttributes.MESSAGING_CONVERSATION_ID] = properties.correlation_id
73+
self._attributes[
74+
SpanAttributes.MESSAGING_CONVERSATION_ID
75+
] = properties.correlation_id
6476

6577
def build(self) -> Optional[Span]:
6678
if not is_instrumentation_enabled():
@@ -69,9 +81,11 @@ def build(self) -> Optional[Span]:
6981
self._attributes[SpanAttributes.MESSAGING_OPERATION] = self._operation.value
7082
else:
7183
self._attributes[SpanAttributes.MESSAGING_TEMP_DESTINATION] = True
72-
span = self._tracer.start_span(self._generate_span_name(), kind=self._kind, attributes=self._attributes)
84+
span = self._tracer.start_span(
85+
self._generate_span_name(), kind=self._kind, attributes=self._attributes
86+
)
7387
return span
7488

7589
def _generate_span_name(self) -> str:
76-
operation_value = self._operation.value if self._operation else 'send'
77-
return f'{self._destination} {operation_value}'
90+
operation_value = self._operation.value if self._operation else "send"
91+
return f"{self._destination} {operation_value}"

instrumentation/opentelemetry-instrumentation-aio-pika/tests/consts.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@
1515
SERVER_URL = URL(
1616
f"amqp://{SERVER_USER}:{SERVER_PASS}@{SERVER_HOST}:{SERVER_PORT}/"
1717
)
18-
CONNECTION = Namespace(connection=Namespace(url=SERVER_URL))
19-
CHANNEL = Namespace(connection=CONNECTION, loop=None)
18+
CONNECTION_7 = Namespace(connection=Namespace(url=SERVER_URL))
19+
CONNECTION_8 = Namespace(url=SERVER_URL)
20+
CHANNEL_7 = Namespace(connection=CONNECTION_7, loop=None)
21+
CHANNEL_8 = Namespace(connection=CONNECTION_8, loop=None)
2022
MESSAGE = Namespace(
2123
properties=Namespace(
2224
message_id=MESSAGE_ID, correlation_id=CORRELATION_ID, headers={}

instrumentation/opentelemetry-instrumentation-aio-pika/tests/test_callback_decorator.py

+48-6
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414
import asyncio
15-
from unittest import TestCase, mock
15+
from unittest import TestCase, mock, skipIf
1616

17-
from aio_pika import Queue
17+
from aio_pika import Queue, version_info
1818

1919
from opentelemetry.instrumentation.aio_pika.callback_decorator import (
2020
CallbackDecorator,
@@ -23,7 +23,8 @@
2323
from opentelemetry.trace import SpanKind, get_tracer
2424

2525
from .consts import (
26-
CHANNEL,
26+
CHANNEL_7,
27+
CHANNEL_8,
2728
CORRELATION_ID,
2829
EXCHANGE_NAME,
2930
MESSAGE,
@@ -35,7 +36,8 @@
3536
)
3637

3738

38-
class TestInstrumentedQueue(TestCase):
39+
@skipIf(version_info >= (8, 0), "Only for aio_pika 7")
40+
class TestInstrumentedQueueAioRmq7(TestCase):
3941
EXPECTED_ATTRIBUTES = {
4042
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
4143
SpanAttributes.MESSAGING_DESTINATION: EXCHANGE_NAME,
@@ -52,7 +54,7 @@ def setUp(self):
5254
asyncio.set_event_loop(self.loop)
5355

5456
def test_get_callback_span(self):
55-
queue = Queue(CHANNEL, QUEUE_NAME, False, False, False, None)
57+
queue = Queue(CHANNEL_7, QUEUE_NAME, False, False, False, None)
5658
tracer = mock.MagicMock()
5759
CallbackDecorator(tracer, queue)._get_span(MESSAGE)
5860
tracer.start_span.assert_called_once_with(
@@ -62,7 +64,47 @@ def test_get_callback_span(self):
6264
)
6365

6466
def test_decorate_callback(self):
65-
queue = Queue(CHANNEL, QUEUE_NAME, False, False, False, None)
67+
queue = Queue(CHANNEL_7, QUEUE_NAME, False, False, False, None)
68+
callback = mock.MagicMock(return_value=asyncio.sleep(0))
69+
with mock.patch.object(
70+
CallbackDecorator, "_get_span"
71+
) as mocked_get_callback_span:
72+
callback_decorator = CallbackDecorator(self.tracer, queue)
73+
decorated_callback = callback_decorator.decorate(callback)
74+
self.loop.run_until_complete(decorated_callback(MESSAGE))
75+
mocked_get_callback_span.assert_called_once()
76+
callback.assert_called_once_with(MESSAGE)
77+
78+
79+
@skipIf(version_info <= (8, 0), "Only for aio_pika 8")
80+
class TestInstrumentedQueueAioRmq8(TestCase):
81+
EXPECTED_ATTRIBUTES = {
82+
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
83+
SpanAttributes.MESSAGING_DESTINATION: EXCHANGE_NAME,
84+
SpanAttributes.NET_PEER_NAME: SERVER_HOST,
85+
SpanAttributes.NET_PEER_PORT: SERVER_PORT,
86+
SpanAttributes.MESSAGING_MESSAGE_ID: MESSAGE_ID,
87+
SpanAttributes.MESSAGING_CONVERSATION_ID: CORRELATION_ID,
88+
SpanAttributes.MESSAGING_OPERATION: "receive",
89+
}
90+
91+
def setUp(self):
92+
self.tracer = get_tracer(__name__)
93+
self.loop = asyncio.new_event_loop()
94+
asyncio.set_event_loop(self.loop)
95+
96+
def test_get_callback_span(self):
97+
queue = Queue(CHANNEL_8, QUEUE_NAME, False, False, False, None)
98+
tracer = mock.MagicMock()
99+
CallbackDecorator(tracer, queue)._get_span(MESSAGE)
100+
tracer.start_span.assert_called_once_with(
101+
f"{EXCHANGE_NAME} receive",
102+
kind=SpanKind.CONSUMER,
103+
attributes=self.EXPECTED_ATTRIBUTES,
104+
)
105+
106+
def test_decorate_callback(self):
107+
queue = Queue(CHANNEL_8, QUEUE_NAME, False, False, False, None)
66108
callback = mock.MagicMock(return_value=asyncio.sleep(0))
67109
with mock.patch.object(
68110
CallbackDecorator, "_get_span"

instrumentation/opentelemetry-instrumentation-aio-pika/tests/test_publish_decorator.py

+63-7
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
# limitations under the License.
1414
import asyncio
1515
from typing import Type
16-
from unittest import TestCase, mock
16+
from unittest import TestCase, mock, skipIf
1717

18-
from aio_pika import Exchange, RobustExchange
18+
from aio_pika import Exchange, RobustExchange, version_info
1919

2020
from opentelemetry.instrumentation.aio_pika.publish_decorator import (
2121
PublishDecorator,
@@ -24,8 +24,10 @@
2424
from opentelemetry.trace import SpanKind, get_tracer
2525

2626
from .consts import (
27-
CHANNEL,
28-
CONNECTION,
27+
CHANNEL_7,
28+
CHANNEL_8,
29+
CONNECTION_7,
30+
CONNECTION_8,
2931
CORRELATION_ID,
3032
EXCHANGE_NAME,
3133
MESSAGE,
@@ -37,7 +39,8 @@
3739
)
3840

3941

40-
class TestInstrumentedExchange(TestCase):
42+
@skipIf(version_info >= (8, 0), "Only for aio_pika 7")
43+
class TestInstrumentedExchangeAioRmq7(TestCase):
4144
EXPECTED_ATTRIBUTES = {
4245
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
4346
SpanAttributes.MESSAGING_DESTINATION: f"{EXCHANGE_NAME},{ROUTING_KEY}",
@@ -54,7 +57,7 @@ def setUp(self):
5457
asyncio.set_event_loop(self.loop)
5558

5659
def test_get_publish_span(self):
57-
exchange = Exchange(CONNECTION, CHANNEL, EXCHANGE_NAME)
60+
exchange = Exchange(CONNECTION_7, CHANNEL_7, EXCHANGE_NAME)
5861
tracer = mock.MagicMock()
5962
PublishDecorator(tracer, exchange)._get_publish_span(
6063
MESSAGE, ROUTING_KEY
@@ -66,7 +69,60 @@ def test_get_publish_span(self):
6669
)
6770

6871
def _test_publish(self, exchange_type: Type[Exchange]):
69-
exchange = exchange_type(CONNECTION, CHANNEL, EXCHANGE_NAME)
72+
exchange = exchange_type(CONNECTION_7, CHANNEL_7, EXCHANGE_NAME)
73+
with mock.patch.object(
74+
PublishDecorator, "_get_publish_span"
75+
) as mock_get_publish_span:
76+
with mock.patch.object(
77+
Exchange, "publish", return_value=asyncio.sleep(0)
78+
) as mock_publish:
79+
decorated_publish = PublishDecorator(
80+
self.tracer, exchange
81+
).decorate(mock_publish)
82+
self.loop.run_until_complete(
83+
decorated_publish(MESSAGE, ROUTING_KEY)
84+
)
85+
mock_publish.assert_called_once()
86+
mock_get_publish_span.assert_called_once()
87+
88+
def test_publish(self):
89+
self._test_publish(Exchange)
90+
91+
def test_robust_publish(self):
92+
self._test_publish(RobustExchange)
93+
94+
95+
@skipIf(version_info <= (8, 0), "Only for aio_pika 8")
96+
class TestInstrumentedExchangeAioRmq8(TestCase):
97+
EXPECTED_ATTRIBUTES = {
98+
SpanAttributes.MESSAGING_SYSTEM: MESSAGING_SYSTEM,
99+
SpanAttributes.MESSAGING_DESTINATION: f"{EXCHANGE_NAME},{ROUTING_KEY}",
100+
SpanAttributes.NET_PEER_NAME: SERVER_HOST,
101+
SpanAttributes.NET_PEER_PORT: SERVER_PORT,
102+
SpanAttributes.MESSAGING_MESSAGE_ID: MESSAGE_ID,
103+
SpanAttributes.MESSAGING_CONVERSATION_ID: CORRELATION_ID,
104+
SpanAttributes.MESSAGING_TEMP_DESTINATION: True,
105+
}
106+
107+
def setUp(self):
108+
self.tracer = get_tracer(__name__)
109+
self.loop = asyncio.new_event_loop()
110+
asyncio.set_event_loop(self.loop)
111+
112+
def test_get_publish_span(self):
113+
exchange = Exchange(CHANNEL_8, EXCHANGE_NAME)
114+
tracer = mock.MagicMock()
115+
PublishDecorator(tracer, exchange)._get_publish_span(
116+
MESSAGE, ROUTING_KEY
117+
)
118+
tracer.start_span.assert_called_once_with(
119+
f"{EXCHANGE_NAME},{ROUTING_KEY} send",
120+
kind=SpanKind.PRODUCER,
121+
attributes=self.EXPECTED_ATTRIBUTES,
122+
)
123+
124+
def _test_publish(self, exchange_type: Type[Exchange]):
125+
exchange = exchange_type(CONNECTION_8, CHANNEL_8, EXCHANGE_NAME)
70126
with mock.patch.object(
71127
PublishDecorator, "_get_publish_span"
72128
) as mock_get_publish_span:

instrumentation/opentelemetry-instrumentation-aio-pika/tests/test_span_builder.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ class TestBuilder(TestCase):
2121
def test_build(self):
2222
builder = SpanBuilder(get_tracer(__name__))
2323
builder.set_as_consumer()
24-
builder.set_destination('destination')
24+
builder.set_destination("destination")
2525
span = builder.build()
2626
self.assertTrue(isinstance(span, Span))

opentelemetry-instrumentation/src/opentelemetry/instrumentation/bootstrap_gen.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
libraries = {
1919
"aio_pika": {
20-
"library": "aio_pika ~= 7.2.0",
20+
"library": "aio_pika >= 7.2.0, < 9.0.0",
2121
"instrumentation": "opentelemetry-instrumentation-aio-pika==0.37b0.dev",
2222
},
2323
"aiohttp": {

tox.ini

+9
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,10 @@ envlist =
203203
py3{7,8,9,10,11}-test-instrumentation-pika{0,1}
204204
pypy3-test-instrumentation-pika{0,1}
205205

206+
; opentelemetry-instrumentation-aio-pika
207+
py3{7,8,9,10,11}-test-instrumentation-aio-pika{7,8}
208+
pypy3-test-instrumentation-aio-pika{7,8}
209+
206210
; opentelemetry-instrumentation-kafka-python
207211
py3{7,8,9,10,11}-test-instrumentation-kafka-python
208212
pypy3-test-instrumentation-kafka-python
@@ -247,6 +251,8 @@ deps =
247251
sqlalchemy14: sqlalchemy~=1.4
248252
pika0: pika>=0.12.0,<1.0.0
249253
pika1: pika>=1.0.0
254+
aio-pika7: aio_pika~=7.2.0
255+
aio-pika8: aio_pika>=8.0.0,<9.0.0
250256
pymemcache135: pymemcache ==1.3.5
251257
pymemcache200: pymemcache >2.0.0,<3.0.0
252258
pymemcache300: pymemcache >3.0.0,<3.4.2
@@ -292,6 +298,7 @@ changedir =
292298
test-instrumentation-logging: instrumentation/opentelemetry-instrumentation-logging/tests
293299
test-instrumentation-mysql: instrumentation/opentelemetry-instrumentation-mysql/tests
294300
test-instrumentation-pika{0,1}: instrumentation/opentelemetry-instrumentation-pika/tests
301+
test-instrumentation-aio-pika{7,8}: instrumentation/opentelemetry-instrumentation-aio-pika/tests
295302
test-instrumentation-psycopg2: instrumentation/opentelemetry-instrumentation-psycopg2/tests
296303
test-instrumentation-pymemcache{135,200,300,342}: instrumentation/opentelemetry-instrumentation-pymemcache/tests
297304
test-instrumentation-pymongo: instrumentation/opentelemetry-instrumentation-pymongo/tests
@@ -333,6 +340,8 @@ commands_pre =
333340

334341
pika{0,1}: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-pika[test]
335342

343+
aio-pika{7,8}: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-aio-pika[test]
344+
336345
kafka-python: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-kafka-python[test]
337346

338347
confluent-kafka: pip install {toxinidir}/instrumentation/opentelemetry-instrumentation-confluent-kafka[test]

0 commit comments

Comments
 (0)