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.
Files changed (13) hide show
  1. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/.gitignore +3 -0
  2. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/LICENSE +1 -1
  3. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/PKG-INFO +7 -3
  4. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/pyproject.toml +6 -2
  5. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/__init__.py +29 -2
  6. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/package.py +1 -1
  7. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/version.py +1 -1
  8. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/tests/test_instrumentation.py +70 -1
  9. opentelemetry_instrumentation_confluent_kafka-0.47b0/tests/utils.py +83 -0
  10. opentelemetry_instrumentation_confluent_kafka-0.45b0/tests/utils.py +0 -39
  11. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/README.rst +0 -0
  12. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/src/opentelemetry/instrumentation/confluent_kafka/utils.py +0 -0
  13. {opentelemetry_instrumentation_confluent_kafka-0.45b0 → opentelemetry_instrumentation_confluent_kafka-0.47b0}/tests/__init__.py +0 -0
@@ -58,3 +58,6 @@ _build/
58
58
  # mypy
59
59
  .mypy_cache/
60
60
  target
61
+
62
+ # Benchmark result files
63
+ *-benchmark.json
@@ -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 The OpenTelemetry Authors
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.45b0
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.45b0
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.3.0,>=1.8.2; extra == 'instruments'
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.45b0",
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.3.0",
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()
@@ -13,4 +13,4 @@
13
13
  # limitations under the License.
14
14
 
15
15
 
16
- _instruments = ("confluent-kafka >= 1.8.2, <= 2.3.0",)
16
+ _instruments = ("confluent-kafka >= 1.8.2, <= 2.4.0",)
@@ -12,4 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- __version__ = "0.45b0"
15
+ __version__ = "0.47b0"
@@ -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