opentelemetry-instrumentation-confluent-kafka 0.45b0__tar.gz → 0.47b0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/.gitignore +3 -0
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/LICENSE +1 -1
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/PKG-INFO +7 -3
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/pyproject.toml +6 -2
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/__init__.py +29 -2
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/package.py +1 -1
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/version.py +1 -1
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/tests/test_instrumentation.py +70 -1
- opentelemetry_instrumentation_confluent_kafka-0.47b0/tests/utils.py +83 -0
- opentelemetry_instrumentation_confluent_kafka-0.45b0/tests/utils.py +0 -39
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/README.rst +0 -0
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/utils.py +0 -0
- {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/tests/__init__.py +0 -0
@@ -186,7 +186,7 @@
|
|
186
186
|
same "printed page" as the copyright notice for easier
|
187
187
|
identification within third-party archives.
|
188
188
|
|
189
|
-
Copyright
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
190
190
|
|
191
191
|
Licensed under the Apache License, Version 2.0 (the "License");
|
192
192
|
you may not use this file except in compliance with the License.
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: opentelemetry-instrumentation-confluent-kafka
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.47b0
|
4
4
|
Summary: OpenTelemetry Confluent Kafka instrumentation
|
5
5
|
Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-confluent-kafka
|
6
6
|
Author-email: OpenTelemetry Authors <cncf-opentelemetry-contributors@lists.cncf.io>
|
@@ -12,12 +12,16 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
12
12
|
Classifier: Programming Language :: Python
|
13
13
|
Classifier: Programming Language :: Python :: 3
|
14
14
|
Classifier: Programming Language :: Python :: 3.8
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
15
19
|
Requires-Python: >=3.8
|
16
20
|
Requires-Dist: opentelemetry-api~=1.12
|
17
|
-
Requires-Dist: opentelemetry-instrumentation==0.
|
21
|
+
Requires-Dist: opentelemetry-instrumentation==0.47b0
|
18
22
|
Requires-Dist: wrapt<2.0.0,>=1.0.0
|
19
23
|
Provides-Extra: instruments
|
20
|
-
Requires-Dist: confluent-kafka<=2.
|
24
|
+
Requires-Dist: confluent-kafka<=2.4.0,>=1.8.2; extra == 'instruments'
|
21
25
|
Description-Content-Type: text/x-rst
|
22
26
|
|
23
27
|
OpenTelemetry confluent-kafka Instrumentation
|
@@ -19,16 +19,20 @@ classifiers = [
|
|
19
19
|
"Programming Language :: Python",
|
20
20
|
"Programming Language :: Python :: 3",
|
21
21
|
"Programming Language :: Python :: 3.8",
|
22
|
+
"Programming Language :: Python :: 3.9",
|
23
|
+
"Programming Language :: Python :: 3.10",
|
24
|
+
"Programming Language :: Python :: 3.11",
|
25
|
+
"Programming Language :: Python :: 3.12",
|
22
26
|
]
|
23
27
|
dependencies = [
|
24
|
-
"opentelemetry-instrumentation == 0.
|
28
|
+
"opentelemetry-instrumentation == 0.47b0",
|
25
29
|
"opentelemetry-api ~= 1.12",
|
26
30
|
"wrapt >= 1.0.0, < 2.0.0",
|
27
31
|
]
|
28
32
|
|
29
33
|
[project.optional-dependencies]
|
30
34
|
instruments = [
|
31
|
-
"confluent-kafka >= 1.8.2, <= 2.
|
35
|
+
"confluent-kafka >= 1.8.2, <= 2.4.0",
|
32
36
|
]
|
33
37
|
|
34
38
|
[project.entry-points.opentelemetry_instrumentor]
|
@@ -144,6 +144,10 @@ class AutoInstrumentedConsumer(Consumer):
|
|
144
144
|
): # pylint: disable=useless-super-delegation
|
145
145
|
return super().consume(*args, **kwargs)
|
146
146
|
|
147
|
+
# This method is deliberately implemented in order to allow wrapt to wrap this function
|
148
|
+
def close(self): # pylint: disable=useless-super-delegation
|
149
|
+
return super().close()
|
150
|
+
|
147
151
|
|
148
152
|
class ProxiedProducer(Producer):
|
149
153
|
def __init__(self, producer: Producer, tracer: Tracer):
|
@@ -151,10 +155,13 @@ class ProxiedProducer(Producer):
|
|
151
155
|
self._tracer = tracer
|
152
156
|
|
153
157
|
def flush(self, timeout=-1):
|
154
|
-
self._producer.flush(timeout)
|
158
|
+
return self._producer.flush(timeout)
|
155
159
|
|
156
160
|
def poll(self, timeout=-1):
|
157
|
-
self._producer.poll(timeout)
|
161
|
+
return self._producer.poll(timeout)
|
162
|
+
|
163
|
+
def purge(self, in_queue=True, in_flight=True, blocking=True):
|
164
|
+
self._producer.purge(in_queue, in_flight, blocking)
|
158
165
|
|
159
166
|
def produce(
|
160
167
|
self, topic, value=None, *args, **kwargs
|
@@ -178,6 +185,11 @@ class ProxiedConsumer(Consumer):
|
|
178
185
|
self._current_consume_span = None
|
179
186
|
self._current_context_token = None
|
180
187
|
|
188
|
+
def close(self):
|
189
|
+
return ConfluentKafkaInstrumentor.wrap_close(
|
190
|
+
self._consumer.close, self
|
191
|
+
)
|
192
|
+
|
181
193
|
def committed(self, partitions, timeout=-1):
|
182
194
|
return self._consumer.committed(partitions, timeout)
|
183
195
|
|
@@ -300,6 +312,9 @@ class ConfluentKafkaInstrumentor(BaseInstrumentor):
|
|
300
312
|
func, instance, self._tracer, args, kwargs
|
301
313
|
)
|
302
314
|
|
315
|
+
def _inner_wrap_close(func, instance):
|
316
|
+
return ConfluentKafkaInstrumentor.wrap_close(func, instance)
|
317
|
+
|
303
318
|
wrapt.wrap_function_wrapper(
|
304
319
|
AutoInstrumentedProducer,
|
305
320
|
"produce",
|
@@ -318,6 +333,12 @@ class ConfluentKafkaInstrumentor(BaseInstrumentor):
|
|
318
333
|
_inner_wrap_consume,
|
319
334
|
)
|
320
335
|
|
336
|
+
wrapt.wrap_function_wrapper(
|
337
|
+
AutoInstrumentedConsumer,
|
338
|
+
"close",
|
339
|
+
_inner_wrap_close,
|
340
|
+
)
|
341
|
+
|
321
342
|
def _uninstrument(self, **kwargs):
|
322
343
|
confluent_kafka.Producer = self._original_kafka_producer
|
323
344
|
confluent_kafka.Consumer = self._original_kafka_consumer
|
@@ -400,3 +421,9 @@ class ConfluentKafkaInstrumentor(BaseInstrumentor):
|
|
400
421
|
)
|
401
422
|
|
402
423
|
return records
|
424
|
+
|
425
|
+
@staticmethod
|
426
|
+
def wrap_close(func, instance):
|
427
|
+
if instance._current_consume_span:
|
428
|
+
_end_current_consume_span(instance)
|
429
|
+
func()
|
@@ -31,7 +31,7 @@ from opentelemetry.semconv.trace import (
|
|
31
31
|
)
|
32
32
|
from opentelemetry.test.test_base import TestBase
|
33
33
|
|
34
|
-
from .utils import MockConsumer, MockedMessage
|
34
|
+
from .utils import MockConsumer, MockedMessage, MockedProducer
|
35
35
|
|
36
36
|
|
37
37
|
class TestConfluentKafka(TestBase):
|
@@ -237,7 +237,44 @@ class TestConfluentKafka(TestBase):
|
|
237
237
|
span_list = self.memory_exporter.get_finished_spans()
|
238
238
|
self._compare_spans(span_list, expected_spans)
|
239
239
|
|
240
|
+
def test_close(self) -> None:
|
241
|
+
instrumentation = ConfluentKafkaInstrumentor()
|
242
|
+
mocked_messages = [
|
243
|
+
MockedMessage("topic-a", 0, 0, []),
|
244
|
+
]
|
245
|
+
expected_spans = [
|
246
|
+
{"name": "recv", "attributes": {}},
|
247
|
+
{
|
248
|
+
"name": "topic-a process",
|
249
|
+
"attributes": {
|
250
|
+
SpanAttributes.MESSAGING_OPERATION: "process",
|
251
|
+
SpanAttributes.MESSAGING_KAFKA_PARTITION: 0,
|
252
|
+
SpanAttributes.MESSAGING_SYSTEM: "kafka",
|
253
|
+
SpanAttributes.MESSAGING_DESTINATION: "topic-a",
|
254
|
+
SpanAttributes.MESSAGING_DESTINATION_KIND: MessagingDestinationKindValues.QUEUE.value,
|
255
|
+
SpanAttributes.MESSAGING_MESSAGE_ID: "topic-a.0.0",
|
256
|
+
},
|
257
|
+
},
|
258
|
+
]
|
259
|
+
|
260
|
+
consumer = MockConsumer(
|
261
|
+
mocked_messages,
|
262
|
+
{
|
263
|
+
"bootstrap.servers": "localhost:29092",
|
264
|
+
"group.id": "mygroup",
|
265
|
+
"auto.offset.reset": "earliest",
|
266
|
+
},
|
267
|
+
)
|
268
|
+
self.memory_exporter.clear()
|
269
|
+
consumer = instrumentation.instrument_consumer(consumer)
|
270
|
+
consumer.poll()
|
271
|
+
consumer.close()
|
272
|
+
|
273
|
+
span_list = self.memory_exporter.get_finished_spans()
|
274
|
+
self._compare_spans(span_list, expected_spans)
|
275
|
+
|
240
276
|
def _compare_spans(self, spans, expected_spans):
|
277
|
+
self.assertEqual(len(spans), len(expected_spans))
|
241
278
|
for span, expected_span in zip(spans, expected_spans):
|
242
279
|
self.assertEqual(expected_span["name"], span.name)
|
243
280
|
for attribute_key, expected_attribute_value in expected_span[
|
@@ -246,3 +283,35 @@ class TestConfluentKafka(TestBase):
|
|
246
283
|
self.assertEqual(
|
247
284
|
expected_attribute_value, span.attributes[attribute_key]
|
248
285
|
)
|
286
|
+
|
287
|
+
def test_producer_poll(self) -> None:
|
288
|
+
instrumentation = ConfluentKafkaInstrumentor()
|
289
|
+
message_queue = []
|
290
|
+
|
291
|
+
producer = MockedProducer(
|
292
|
+
message_queue,
|
293
|
+
{
|
294
|
+
"bootstrap.servers": "localhost:29092",
|
295
|
+
},
|
296
|
+
)
|
297
|
+
|
298
|
+
producer = instrumentation.instrument_producer(producer)
|
299
|
+
producer.produce(topic="topic-1", key="key-1", value="value-1")
|
300
|
+
msg = producer.poll()
|
301
|
+
self.assertIsNotNone(msg)
|
302
|
+
|
303
|
+
def test_producer_flush(self) -> None:
|
304
|
+
instrumentation = ConfluentKafkaInstrumentor()
|
305
|
+
message_queue = []
|
306
|
+
|
307
|
+
producer = MockedProducer(
|
308
|
+
message_queue,
|
309
|
+
{
|
310
|
+
"bootstrap.servers": "localhost:29092",
|
311
|
+
},
|
312
|
+
)
|
313
|
+
|
314
|
+
producer = instrumentation.instrument_producer(producer)
|
315
|
+
producer.produce(topic="topic-1", key="key-1", value="value-1")
|
316
|
+
msg = producer.flush()
|
317
|
+
self.assertIsNotNone(msg)
|
@@ -0,0 +1,83 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from confluent_kafka import Consumer, Producer
|
4
|
+
|
5
|
+
|
6
|
+
class MockConsumer(Consumer):
|
7
|
+
def __init__(self, queue, config):
|
8
|
+
self._queue = queue
|
9
|
+
super().__init__(config)
|
10
|
+
|
11
|
+
def consume(
|
12
|
+
self, num_messages=1, *args, **kwargs
|
13
|
+
): # pylint: disable=keyword-arg-before-vararg
|
14
|
+
messages = self._queue[:num_messages]
|
15
|
+
self._queue = self._queue[num_messages:]
|
16
|
+
return messages
|
17
|
+
|
18
|
+
def poll(self, timeout=None):
|
19
|
+
if len(self._queue) > 0:
|
20
|
+
return self._queue.pop(0)
|
21
|
+
return None
|
22
|
+
|
23
|
+
|
24
|
+
class MockedMessage:
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
topic: str,
|
28
|
+
partition: int,
|
29
|
+
offset: int,
|
30
|
+
headers,
|
31
|
+
key: Optional[str] = None,
|
32
|
+
value: Optional[str] = None,
|
33
|
+
):
|
34
|
+
self._topic = topic
|
35
|
+
self._partition = partition
|
36
|
+
self._offset = offset
|
37
|
+
self._headers = headers
|
38
|
+
self._key = key
|
39
|
+
self._value = value
|
40
|
+
|
41
|
+
def topic(self):
|
42
|
+
return self._topic
|
43
|
+
|
44
|
+
def partition(self):
|
45
|
+
return self._partition
|
46
|
+
|
47
|
+
def offset(self):
|
48
|
+
return self._offset
|
49
|
+
|
50
|
+
def headers(self):
|
51
|
+
return self._headers
|
52
|
+
|
53
|
+
def key(self):
|
54
|
+
return self._key
|
55
|
+
|
56
|
+
def value(self):
|
57
|
+
return self._value
|
58
|
+
|
59
|
+
|
60
|
+
class MockedProducer(Producer):
|
61
|
+
def __init__(self, queue, config):
|
62
|
+
self._queue = queue
|
63
|
+
super().__init__(config)
|
64
|
+
|
65
|
+
def produce(
|
66
|
+
self, *args, **kwargs
|
67
|
+
): # pylint: disable=keyword-arg-before-vararg
|
68
|
+
self._queue.append(
|
69
|
+
MockedMessage(
|
70
|
+
topic=kwargs.get("topic"),
|
71
|
+
partition=0,
|
72
|
+
offset=0,
|
73
|
+
headers=[],
|
74
|
+
key=kwargs.get("key"),
|
75
|
+
value=kwargs.get("value"),
|
76
|
+
)
|
77
|
+
)
|
78
|
+
|
79
|
+
def poll(self, *args, **kwargs):
|
80
|
+
return len(self._queue)
|
81
|
+
|
82
|
+
def flush(self, *args, **kwargs):
|
83
|
+
return len(self._queue)
|
@@ -1,39 +0,0 @@
|
|
1
|
-
from confluent_kafka import Consumer
|
2
|
-
|
3
|
-
|
4
|
-
class MockConsumer(Consumer):
|
5
|
-
def __init__(self, queue, config):
|
6
|
-
self._queue = queue
|
7
|
-
super().__init__(config)
|
8
|
-
|
9
|
-
def consume(
|
10
|
-
self, num_messages=1, *args, **kwargs
|
11
|
-
): # pylint: disable=keyword-arg-before-vararg
|
12
|
-
messages = self._queue[:num_messages]
|
13
|
-
self._queue = self._queue[num_messages:]
|
14
|
-
return messages
|
15
|
-
|
16
|
-
def poll(self, timeout=None):
|
17
|
-
if len(self._queue) > 0:
|
18
|
-
return self._queue.pop(0)
|
19
|
-
return None
|
20
|
-
|
21
|
-
|
22
|
-
class MockedMessage:
|
23
|
-
def __init__(self, topic: str, partition: int, offset: int, headers):
|
24
|
-
self._topic = topic
|
25
|
-
self._partition = partition
|
26
|
-
self._offset = offset
|
27
|
-
self._headers = headers
|
28
|
-
|
29
|
-
def topic(self):
|
30
|
-
return self._topic
|
31
|
-
|
32
|
-
def partition(self):
|
33
|
-
return self._partition
|
34
|
-
|
35
|
-
def offset(self):
|
36
|
-
return self._offset
|
37
|
-
|
38
|
-
def headers(self):
|
39
|
-
return self._headers
|
File without changes
|
File without changes
|