diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index f24edcf137..2b39e0d7ad 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1848,9 +1848,21 @@ def apply_to_telemetry(self, telemetry: "Union[Log, Metric, StreamedSpan]") -> N telemetry["trace_id"] = ( trace_id or "00000000-0000-0000-0000-000000000000" ) - span_id = trace_context.get("span_id") - if telemetry.get("span_id") is None and span_id: - telemetry["span_id"] = span_id + + # span_id should only be populated if there's an active span. We can't + # use the trace_context here because it synthesizes a span_id if there + # isn't one + if telemetry.get("span_id") is None: + if self._span is not None and not isinstance( + self._span, NoOpStreamedSpan + ): + telemetry["span_id"] = self._span.span_id + else: + external_propagation_context = get_external_propagation_context() + if external_propagation_context: + _, span_id = external_propagation_context + if span_id is not None: + telemetry["span_id"] = span_id self._apply_scope_attributes_to_telemetry(telemetry) self._apply_user_attributes_to_telemetry(telemetry) diff --git a/tests/integrations/logging/test_logging.py b/tests/integrations/logging/test_logging.py index 5e384bd3db..12dbacc9ec 100644 --- a/tests/integrations/logging/test_logging.py +++ b/tests/integrations/logging/test_logging.py @@ -486,7 +486,7 @@ 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) + assert logs[0]["span_id"] is None attributes = logs[0]["attributes"] diff --git a/tests/integrations/loguru/test_loguru.py b/tests/integrations/loguru/test_loguru.py index 66cc336de5..6fdc0cb1aa 100644 --- a/tests/integrations/loguru/test_loguru.py +++ b/tests/integrations/loguru/test_loguru.py @@ -419,7 +419,7 @@ def test_logger_with_all_attributes( logs = envelopes_to_logs(envelopes) assert "span_id" in logs[0] - assert isinstance(logs[0]["span_id"], str) + assert logs[0]["span_id"] is None attributes = logs[0]["attributes"] diff --git a/tests/test_logs.py b/tests/test_logs.py index 86861cfe90..34741c05d6 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -53,7 +53,7 @@ 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"], + "span_id": log_json.get("span_id"), } res.append(log) return res @@ -324,6 +324,24 @@ def test_logs_tied_to_transactions(sentry_init, capture_envelopes): assert logs[0]["span_id"] == trx.span_id +@minimum_python_37 +def test_logs_no_span_id_without_active_span(sentry_init, capture_envelopes): + """ + Per the metrics spec, span_id is only attached when a span is active + when the telemetry is emitted. The propagation context's synthesized + span_id must not be used as a fallback. + """ + sentry_init(enable_logs=True) + envelopes = capture_envelopes() + + sentry_sdk.logger.warning("This is a log without an active span") + + get_client().flush() + logs = envelopes_to_logs(envelopes) + assert logs[0]["trace_id"] is not None + assert logs[0]["span_id"] is None + + @minimum_python_37 def test_logs_tied_to_spans(sentry_init, capture_envelopes): """ diff --git a/tests/test_metrics.py b/tests/test_metrics.py index 3ad3f6042d..f528cac35f 100644 --- a/tests/test_metrics.py +++ b/tests/test_metrics.py @@ -184,7 +184,10 @@ def test_metrics_tracing_without_performance(sentry_init, capture_envelopes): propagation_context = isolation_scope._propagation_context assert propagation_context is not None assert metrics[0]["trace_id"] == propagation_context.trace_id - assert metrics[0]["span_id"] == propagation_context.span_id + # Per the metrics spec, span_id is only attached when a span is + # active when the metric is emitted. The propagation context's + # synthesized span_id must not be used as a fallback. + assert metrics[0]["span_id"] is None def test_metrics_before_send(sentry_init, capture_envelopes): @@ -294,7 +297,6 @@ def test_transport_format(sentry_init, capture_envelopes): "value": 1, "timestamp": mock.ANY, "trace_id": mock.ANY, - "span_id": mock.ANY, "attributes": { "sentry.environment": { "type": "string",