Repository: open-telemetry/opentelemetry-python-contrib

Issue:
Confluent kafka Producer doesn't return expected values from poll() and flush() methods when instrumented by ConfluentKafkaInstrumentor().instrument_producer()
**Describe your environment** 
```
python 3.9
confluent-kafka==2.3.0
opentelemetry-instrumentation-confluent-kafka==0.45b0
opentelemetry-semantic-conventions==0.45b0
```

**Steps to reproduce**
Spin up a kafka broker:
```
docker run -d --name kafka-server --hostname kafka-server \
    -e KAFKA_CFG_NODE_ID=0 \
    -e KAFKA_CFG_PROCESS_ROLES=controller,broker \
    -e KAFKA_CFG_LISTENERS=PLAINTEXT://:9092,CONTROLLER://:9093 \
    -e KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT \
    -e KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=0@kafka-server:9093 \
    -e KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER \
    bitnami/kafka:latest
```

Run this code:
```
from opentelemetry.instrumentation.confluent_kafka import ConfluentKafkaInstrumentor
from confluent_kafka import Producer

inst = ConfluentKafkaInstrumentor()
p = confluent_kafka.Producer({'bootstrap.servers': 'localhost:9092'})
p = inst.instrument_producer(p)
p.produce('my-topic', b'raw_bytes')
msg = p.poll(0)
print(msg)
msg_pending = p.flush5)
print(msg_pending)
```

**What is the expected behavior?**
Should be printed:
```
0
1
```
Producer poll() and flush() methods should return integers.

**What is the actual behavior?**
```
None
None
```
Producer poll() and flush() methods are returning `None`

**Additional context**

flush and poll methods doesn't have a return statement:

https://github.com/open-telemetry/opentelemetry-python-contrib/blob/460fc335836c395db8472ecf464e7ecd94c08925/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py#L154

https://github.com/open-telemetry/opentelemetry-python-contrib/blob/460fc335836c395db8472ecf464e7ecd94c08925/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py#L157



Discussion:

Resolving patch:
diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py
--- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/src/opentelemetry/instrumentation/confluent_kafka/__init__.py
@@ -151,10 +151,10 @@ def __init__(self, producer: Producer, tracer: Tracer):
         self._tracer = tracer
 
     def flush(self, timeout=-1):
-        self._producer.flush(timeout)
+        return self._producer.flush(timeout)
 
     def poll(self, timeout=-1):
-        self._producer.poll(timeout)
+        return self._producer.poll(timeout)
 
     def produce(
         self, topic, value=None, *args, **kwargs


Test patch:
diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py b/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py
--- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py
+++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/test_instrumentation.py
@@ -31,7 +31,7 @@
 )
 from opentelemetry.test.test_base import TestBase
 
-from .utils import MockConsumer, MockedMessage
+from .utils import MockConsumer, MockedMessage, MockedProducer
 
 
 class TestConfluentKafka(TestBase):
@@ -246,3 +246,35 @@ def _compare_spans(self, spans, expected_spans):
                 self.assertEqual(
                     expected_attribute_value, span.attributes[attribute_key]
                 )
+
+    def test_producer_poll(self) -> None:
+        instrumentation = ConfluentKafkaInstrumentor()
+        message_queue = []
+
+        producer = MockedProducer(
+            message_queue,
+            {
+                "bootstrap.servers": "localhost:29092",
+            },
+        )
+
+        producer = instrumentation.instrument_producer(producer)
+        producer.produce(topic="topic-1", key="key-1", value="value-1")
+        msg = producer.poll()
+        self.assertIsNotNone(msg)
+
+    def test_producer_flush(self) -> None:
+        instrumentation = ConfluentKafkaInstrumentor()
+        message_queue = []
+
+        producer = MockedProducer(
+            message_queue,
+            {
+                "bootstrap.servers": "localhost:29092",
+            },
+        )
+
+        producer = instrumentation.instrument_producer(producer)
+        producer.produce(topic="topic-1", key="key-1", value="value-1")
+        msg = producer.flush()
+        self.assertIsNotNone(msg)
diff --git a/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/utils.py b/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/utils.py
--- a/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/utils.py
+++ b/instrumentation/opentelemetry-instrumentation-confluent-kafka/tests/utils.py
@@ -1,4 +1,6 @@
-from confluent_kafka import Consumer
+from typing import Optional
+
+from confluent_kafka import Consumer, Producer
 
 
 class MockConsumer(Consumer):
@@ -20,11 +22,21 @@ def poll(self, timeout=None):
 
 
 class MockedMessage:
-    def __init__(self, topic: str, partition: int, offset: int, headers):
+    def __init__(
+        self,
+        topic: str,
+        partition: int,
+        offset: int,
+        headers,
+        key: Optional[str] = None,
+        value: Optional[str] = None,
+    ):
         self._topic = topic
         self._partition = partition
         self._offset = offset
         self._headers = headers
+        self._key = key
+        self._value = value
 
     def topic(self):
         return self._topic
@@ -37,3 +49,35 @@ def offset(self):
 
     def headers(self):
         return self._headers
+
+    def key(self):
+        return self._key
+
+    def value(self):
+        return self._value
+
+
+class MockedProducer(Producer):
+    def __init__(self, queue, config):
+        self._queue = queue
+        super().__init__(config)
+
+    def produce(
+        self, *args, **kwargs
+    ):  # pylint: disable=keyword-arg-before-vararg
+        self._queue.append(
+            MockedMessage(
+                topic=kwargs.get("topic"),
+                partition=0,
+                offset=0,
+                headers=[],
+                key=kwargs.get("key"),
+                value=kwargs.get("value"),
+            )
+        )
+
+    def poll(self, *args, **kwargs):
+        return len(self._queue)
+
+    def flush(self, *args, **kwargs):
+        return len(self._queue)