From cb13467d66f924272e3ca00e5424d42c4336b600 Mon Sep 17 00:00:00 2001 From: Piotr Wolski Date: Fri, 29 May 2026 09:46:06 -0600 Subject: [PATCH 1/4] fix(aiokafka): preserve partition=0 / offset=0 on consumer spans The aiokafka getone handler reported `kafka.partition` and `kafka.message_offset` as -1 whenever the real value was 0, because it used `value or -1` which treats the integer 0 as falsy. Single-partition topics and the first message in any partition were therefore tagged with -1. Replace with explicit None checks so that legitimate zero values pass through unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- ddtrace/_trace/trace_handlers.py | 4 ++-- .../aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml diff --git a/ddtrace/_trace/trace_handlers.py b/ddtrace/_trace/trace_handlers.py index 254c4f7795e..b8eaa1d2b8f 100644 --- a/ddtrace/_trace/trace_handlers.py +++ b/ddtrace/_trace/trace_handlers.py @@ -1396,7 +1396,7 @@ def _on_aiokafka_getone_message( if message is not None: message_key = message.key.decode("utf-8") if message.key else None - message_offset = message.offset or -1 + message_offset = message.offset if message.offset is not None else -1 topic = str(message.topic) span._set_attribute(TOPIC, topic) span._set_attribute(TOMBSTONE, str(message.value is None)) @@ -1404,7 +1404,7 @@ def _on_aiokafka_getone_message( if isinstance(message_key, str): span.set_tag(MESSAGE_KEY, message_key) - span._set_attribute(PARTITION, message.partition or -1) + span._set_attribute(PARTITION, message.partition if message.partition is not None else -1) span._set_attribute(MESSAGE_OFFSET, message_offset) if err is not None: diff --git a/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml b/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml new file mode 100644 index 00000000000..b76c3dc931f --- /dev/null +++ b/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + aiokafka: Fixes ``kafka.partition`` and ``kafka.message_offset`` consumer + span tags being reported as ``-1`` when the actual value was ``0`` (e.g. + single-partition topics or the first message in a partition). The previous + code used ``value or -1``, which treated falsy ``0`` as missing. From 2aa724f34952c65214d29cc00110c0c614b6b143 Mon Sep 17 00:00:00 2001 From: Piotr Wolski Date: Fri, 29 May 2026 12:49:37 -0600 Subject: [PATCH 2/4] docs: clarify release note wording --- .../notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml b/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml index b76c3dc931f..2d932d32a6a 100644 --- a/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml +++ b/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml @@ -4,4 +4,4 @@ fixes: aiokafka: Fixes ``kafka.partition`` and ``kafka.message_offset`` consumer span tags being reported as ``-1`` when the actual value was ``0`` (e.g. single-partition topics or the first message in a partition). The previous - code used ``value or -1``, which treated falsy ``0`` as missing. + code used ``value or -1``, which treated the falsy integer ``0`` as missing. From 0220612045dc430fa26f0af4501b80dbd00744f2 Mon Sep 17 00:00:00 2001 From: Piotr Wolski Date: Tue, 2 Jun 2026 13:00:44 -0600 Subject: [PATCH 3/4] fix(aiokafka): omit partition/offset tags when value is None instead of using -1 Addresses reviewer feedback: -1 as a sentinel for unknown partition or offset provides no real value and is misleading. Producer spans (where partition is not yet known before broker assignment) and any span with a genuinely absent value now omit these tags entirely. Co-Authored-By: Claude Sonnet 4.6 --- ddtrace/_trace/trace_handlers.py | 10 ++++++---- ...iokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml | 9 +++++---- ...kafka.test_aiokafka.test_send_and_wait_failure.json | 1 - 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ddtrace/_trace/trace_handlers.py b/ddtrace/_trace/trace_handlers.py index b8eaa1d2b8f..4903f250c98 100644 --- a/ddtrace/_trace/trace_handlers.py +++ b/ddtrace/_trace/trace_handlers.py @@ -1354,7 +1354,8 @@ def _on_aiokafka_send_start( span._set_attribute(SPAN_KIND, SpanKind.PRODUCER) span._set_attribute(TOMBSTONE, str(send_value is None)) span.set_tag(MESSAGE_KEY, send_key.decode("utf-8") if send_key else None) - span._set_attribute(PARTITION, partition or -1) + if partition is not None: + span._set_attribute(PARTITION, partition) span._set_attribute(_SPAN_MEASURED_KEY, 1) if config.aiokafka.distributed_tracing_enabled: @@ -1396,7 +1397,6 @@ def _on_aiokafka_getone_message( if message is not None: message_key = message.key.decode("utf-8") if message.key else None - message_offset = message.offset if message.offset is not None else -1 topic = str(message.topic) span._set_attribute(TOPIC, topic) span._set_attribute(TOMBSTONE, str(message.value is None)) @@ -1404,8 +1404,10 @@ def _on_aiokafka_getone_message( if isinstance(message_key, str): span.set_tag(MESSAGE_KEY, message_key) - span._set_attribute(PARTITION, message.partition if message.partition is not None else -1) - span._set_attribute(MESSAGE_OFFSET, message_offset) + if message.partition is not None: + span._set_attribute(PARTITION, message.partition) + if message.offset is not None: + span._set_attribute(MESSAGE_OFFSET, message.offset) if err is not None: span.set_exc_info(type(err), err, err.__traceback__) diff --git a/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml b/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml index 2d932d32a6a..a02391d0240 100644 --- a/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml +++ b/releasenotes/notes/aiokafka-zero-partition-offset-50aa5b8c6766e3ff.yaml @@ -1,7 +1,8 @@ --- fixes: - | - aiokafka: Fixes ``kafka.partition`` and ``kafka.message_offset`` consumer - span tags being reported as ``-1`` when the actual value was ``0`` (e.g. - single-partition topics or the first message in a partition). The previous - code used ``value or -1``, which treated the falsy integer ``0`` as missing. + aiokafka: Fixes ``kafka.partition`` and ``kafka.message_offset`` span tags + being incorrectly set to ``-1``. Consumer spans no longer misreport ``0`` + (e.g. single-partition topics or the first message in a partition) due to + falsy evaluation. Producer spans and any span where the value is genuinely + unknown now omit these tags entirely rather than emitting a misleading ``-1``. diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_failure.json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_failure.json index acbaa772e29..28ba2ad0555 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_failure.json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_failure.json @@ -35,7 +35,6 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.partition": -1.0, "process_id": 510.0 }, "duration": 104618323, From 2dcff6d668287926b4cfc397cbfa0fc84602bb11 Mon Sep 17 00:00:00 2001 From: Piotr Wolski Date: Tue, 2 Jun 2026 14:57:09 -0600 Subject: [PATCH 4/4] test(aiokafka): fix snapshot consumer spans to show real partition/offset values After rebasing, consumer span snapshots were reset to main's state which still had the stale -1.0 sentinel values. The code correctly produces 0.0 (first message, single partition), so update snapshots to match. Co-Authored-By: Claude Sonnet 4.6 --- ...a.test_aiokafka.test_send_and_wait_commit_with_offset.json | 4 ++-- ...b.aiokafka.test_aiokafka.test_send_and_wait_key[None].json | 4 ++-- ...okafka.test_aiokafka.test_send_and_wait_key[test_key].json | 4 ++-- ...aiokafka.test_aiokafka.test_send_and_wait_value[None].json | 4 ++-- ...est_aiokafka.test_send_and_wait_value[hueh_hueh_hueh].json | 4 ++-- ..._aiokafka.test_send_and_wait_with_distributed_tracing.json | 4 ++-- ...tests.contrib.aiokafka.test_aiokafka.test_send_commit.json | 4 ++-- ....test_aiokafka_dsm.test_data_streams_aiokafka_enabled.json | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_commit_with_offset.json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_commit_with_offset.json index 153c8991d05..703e774bf12 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_commit_with_offset.json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_commit_with_offset.json @@ -34,8 +34,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 510.0 }, "duration": 5840561, diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[None].json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[None].json index 510f327f402..ca71b44efd0 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[None].json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[None].json @@ -33,8 +33,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 510.0 }, "duration": 2732458, diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[test_key].json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[test_key].json index 20b978f4361..1cf6f041ff1 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[test_key].json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_key[test_key].json @@ -34,8 +34,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 510.0 }, "duration": 4094210, diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[None].json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[None].json index 5a097359c30..67a7a6cb51e 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[None].json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[None].json @@ -34,8 +34,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 510.0 }, "duration": 3528046, diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[hueh_hueh_hueh].json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[hueh_hueh_hueh].json index cf99900d7eb..a4746c72227 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[hueh_hueh_hueh].json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_value[hueh_hueh_hueh].json @@ -34,8 +34,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 510.0 }, "duration": 2561725, diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_with_distributed_tracing.json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_with_distributed_tracing.json index 6697cdd3e80..59ce602dcd9 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_with_distributed_tracing.json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_and_wait_with_distributed_tracing.json @@ -75,8 +75,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 510.0 }, "duration": 1800834, diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_commit.json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_commit.json index 3cda6790555..b8222dd4e58 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_commit.json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka.test_send_commit.json @@ -34,8 +34,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 510.0 }, "duration": 2675113, diff --git a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka_dsm.test_data_streams_aiokafka_enabled.json b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka_dsm.test_data_streams_aiokafka_enabled.json index e61e85420d2..f71dab500f1 100644 --- a/tests/snapshots/tests.contrib.aiokafka.test_aiokafka_dsm.test_data_streams_aiokafka_enabled.json +++ b/tests/snapshots/tests.contrib.aiokafka.test_aiokafka_dsm.test_data_streams_aiokafka_enabled.json @@ -74,8 +74,8 @@ "_dd.top_level": 1.0, "_dd.tracer_kr": 1.0, "_sampling_priority_v1": 1.0, - "kafka.message_offset": -1.0, - "kafka.partition": -1.0, + "kafka.message_offset": 0.0, + "kafka.partition": 0.0, "process_id": 648.0 }, "duration": 499024,