From c5f090330e83c75e0d482419e448bce9a1f9e662 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 16 Dec 2025 12:32:15 +0100 Subject: [PATCH 1/6] fix(logs): Set span_id instead of sentry.trace.parent_span_id --- sentry_sdk/client.py | 7 ++----- tests/integrations/logging/test_logging.py | 7 +++---- tests/integrations/loguru/test_loguru.py | 7 +++---- tests/test_logs.py | 11 +++++------ 4 files changed, 13 insertions(+), 19 deletions(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index aecbd68ccd..47d60c9dea 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -927,11 +927,8 @@ def _capture_log(self, log: "Optional[Log]") -> None: if trace_id is not None and log.get("trace_id") is None: log["trace_id"] = trace_id - if ( - span_id is not None - and "sentry.trace.parent_span_id" not in log["attributes"] - ): - log["attributes"]["sentry.trace.parent_span_id"] = span_id + if span_id is not None and not log.get("span_id"): + log["attributes"]["span_id"] = span_id # The user, if present, is always set on the isolation scope. if isolation_scope._user is not None: diff --git a/tests/integrations/logging/test_logging.py b/tests/integrations/logging/test_logging.py index e7849253d6..7b144f4b55 100644 --- a/tests/integrations/logging/test_logging.py +++ b/tests/integrations/logging/test_logging.py @@ -438,6 +438,9 @@ def test_logger_with_all_attributes(sentry_init, capture_envelopes): logs = envelopes_to_logs(envelopes) + assert "span_id" in logs[0] + assert isinstance(logs[0]["span_id"], str) + attributes = logs[0]["attributes"] assert "process.pid" in attributes @@ -478,10 +481,6 @@ def test_logger_with_all_attributes(sentry_init, capture_envelopes): assert attributes.pop("sentry.sdk.name").startswith("sentry.python") - assert "sentry.trace.parent_span_id" in attributes - assert isinstance(attributes["sentry.trace.parent_span_id"], str) - del attributes["sentry.trace.parent_span_id"] - # Assert on the remaining non-dynamic attributes. assert attributes == { "foo": "bar", diff --git a/tests/integrations/loguru/test_loguru.py b/tests/integrations/loguru/test_loguru.py index ed7650700f..7a062ed9c5 100644 --- a/tests/integrations/loguru/test_loguru.py +++ b/tests/integrations/loguru/test_loguru.py @@ -420,6 +420,9 @@ def test_logger_with_all_attributes( attributes = logs[0]["attributes"] + assert "span_id" in logs[0] + assert isinstance(logs[0]["span_id"], str) + assert "process.pid" in attributes assert isinstance(attributes["process.pid"], int) del attributes["process.pid"] @@ -458,10 +461,6 @@ def test_logger_with_all_attributes( assert attributes.pop("sentry.sdk.name").startswith("sentry.python") - assert "sentry.trace.parent_span_id" in attributes - assert isinstance(attributes["sentry.trace.parent_span_id"], str) - del attributes["sentry.trace.parent_span_id"] - # Assert on the remaining non-dynamic attributes. assert attributes == { "logger.name": "tests.integrations.loguru.test_loguru", diff --git a/tests/test_logs.py b/tests/test_logs.py index 15baa9328b..5373ded435 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -319,7 +319,9 @@ def test_logs_tied_to_transactions(sentry_init, capture_envelopes): get_client().flush() logs = envelopes_to_logs(envelopes) - assert logs[0]["attributes"]["sentry.trace.parent_span_id"] == trx.span_id + + assert "span_id" in logs[0] + assert logs[0]["span_id"] == trx.span_id @minimum_python_37 @@ -336,7 +338,7 @@ def test_logs_tied_to_spans(sentry_init, capture_envelopes): get_client().flush() logs = envelopes_to_logs(envelopes) - assert logs[0]["attributes"]["sentry.trace.parent_span_id"] == span.span_id + assert logs[0]["span_id"] == span.span_id def test_auto_flush_logs_after_100(sentry_init, capture_envelopes): @@ -491,6 +493,7 @@ def record_lost_event(reason, data_category=None, item=None, *, quantity=1): "level": "info", "timestamp": mock.ANY, "trace_id": mock.ANY, + "span_id": mock.ANY, "attributes": { "sentry.environment": { "type": "string", @@ -516,10 +519,6 @@ def record_lost_event(reason, data_category=None, item=None, *, quantity=1): "type": "string", "value": "info", }, - "sentry.trace.parent_span_id": { - "type": "string", - "value": mock.ANY, - }, "server.address": { "type": "string", "value": "test-server", From 68eef78fc737a5311b54a60c3ba32451ba23018e Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 16 Dec 2025 12:36:49 +0100 Subject: [PATCH 2/6] fix --- sentry_sdk/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index 47d60c9dea..bea8d2248b 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -928,7 +928,7 @@ def _capture_log(self, log: "Optional[Log]") -> None: log["trace_id"] = trace_id if span_id is not None and not log.get("span_id"): - log["attributes"]["span_id"] = span_id + log["span_id"] = span_id # The user, if present, is always set on the isolation scope. if isolation_scope._user is not None: From 15ce1df2e0f9f053e1a4e4f6f1a1eced8c8b3ccb Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 16 Dec 2025 12:50:49 +0100 Subject: [PATCH 3/6] tweaks --- sentry_sdk/_log_batcher.py | 1 + sentry_sdk/_types.py | 1 + sentry_sdk/client.py | 2 +- sentry_sdk/integrations/loguru.py | 1 + tests/integrations/loguru/test_loguru.py | 4 ++-- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/_log_batcher.py b/sentry_sdk/_log_batcher.py index b0a5f75d46..aee9b1db6f 100644 --- a/sentry_sdk/_log_batcher.py +++ b/sentry_sdk/_log_batcher.py @@ -134,6 +134,7 @@ def format_attribute(val: "int | float | str | bool") -> "Any": res = { "timestamp": int(log["time_unix_nano"]) / 1.0e9, "trace_id": log.get("trace_id", "00000000-0000-0000-0000-000000000000"), + "span_id": log.get("span_id"), "level": str(log["severity_text"]), "body": str(log["body"]), "attributes": { diff --git a/sentry_sdk/_types.py b/sentry_sdk/_types.py index c5bc1366ff..5497a27a3d 100644 --- a/sentry_sdk/_types.py +++ b/sentry_sdk/_types.py @@ -224,6 +224,7 @@ class SDKInfo(TypedDict): "attributes": dict[str, str | bool | float | int], "time_unix_nano": int, "trace_id": Optional[str], + "span_id": Optional[str], }, ) diff --git a/sentry_sdk/client.py b/sentry_sdk/client.py index bea8d2248b..c51848e9af 100644 --- a/sentry_sdk/client.py +++ b/sentry_sdk/client.py @@ -927,7 +927,7 @@ def _capture_log(self, log: "Optional[Log]") -> None: if trace_id is not None and log.get("trace_id") is None: log["trace_id"] = trace_id - if span_id is not None and not log.get("span_id"): + if span_id is not None and log.get("span_id") is None: log["span_id"] = span_id # The user, if present, is always set on the isolation scope. diff --git a/sentry_sdk/integrations/loguru.py b/sentry_sdk/integrations/loguru.py index 6c4da26c48..87e154d283 100644 --- a/sentry_sdk/integrations/loguru.py +++ b/sentry_sdk/integrations/loguru.py @@ -204,5 +204,6 @@ def loguru_sentry_logs_handler(message: "Message") -> None: "attributes": attrs, "time_unix_nano": int(record["time"].timestamp() * 1e9), "trace_id": None, + "span_id": None, } ) diff --git a/tests/integrations/loguru/test_loguru.py b/tests/integrations/loguru/test_loguru.py index 7a062ed9c5..66cc336de5 100644 --- a/tests/integrations/loguru/test_loguru.py +++ b/tests/integrations/loguru/test_loguru.py @@ -418,11 +418,11 @@ def test_logger_with_all_attributes( logs = envelopes_to_logs(envelopes) - attributes = logs[0]["attributes"] - assert "span_id" in logs[0] assert isinstance(logs[0]["span_id"], str) + attributes = logs[0]["attributes"] + assert "process.pid" in attributes assert isinstance(attributes["process.pid"], int) del attributes["process.pid"] From 118af39132ee09ec151da25dfb118b03dc2c42ec Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 16 Dec 2025 13:00:22 +0100 Subject: [PATCH 4/6] . --- tests/test_logs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_logs.py b/tests/test_logs.py index 5373ded435..e6eb4ea718 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -55,7 +55,9 @@ def envelopes_to_logs(envelopes: List[Envelope]) -> List[Log]: "attributes": otel_attributes_to_dict(log_json["attributes"]), "time_unix_nano": int(float(log_json["timestamp"]) * 1e9), "trace_id": log_json["trace_id"], + "span_id": log_json["span_id"], } # type: Log + res.append(log) return res From ddaa8ee40220bd51320edd489cd495b14f152973 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 16 Dec 2025 13:04:24 +0100 Subject: [PATCH 5/6] . --- tests/test_logs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_logs.py b/tests/test_logs.py index e6eb4ea718..d82c92886e 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -144,6 +144,7 @@ def _before_log(record, hint): "attributes", "time_unix_nano", "trace_id", + "span_id", } if record["severity_text"] in ["fatal", "error"]: From bc95e310ebeec736ab7129d5b7915e8e3156aa91 Mon Sep 17 00:00:00 2001 From: Ivana Kellyer Date: Tue, 16 Dec 2025 13:07:56 +0100 Subject: [PATCH 6/6] . --- sentry_sdk/integrations/logging.py | 1 + sentry_sdk/logger.py | 1 + 2 files changed, 2 insertions(+) diff --git a/sentry_sdk/integrations/logging.py b/sentry_sdk/integrations/logging.py index 6492526c21..97fc99de0a 100644 --- a/sentry_sdk/integrations/logging.py +++ b/sentry_sdk/integrations/logging.py @@ -404,5 +404,6 @@ def _capture_log_from_record( "attributes": attrs, "time_unix_nano": int(record.created * 1e9), "trace_id": None, + "span_id": None, }, ) diff --git a/sentry_sdk/logger.py b/sentry_sdk/logger.py index 0c8c658881..afdad436ef 100644 --- a/sentry_sdk/logger.py +++ b/sentry_sdk/logger.py @@ -66,6 +66,7 @@ def _capture_log( "body": body, "time_unix_nano": time.time_ns(), "trace_id": None, + "span_id": None, }, )