Skip to content

Commit f1d41ee

Browse files
committed
fix(tracing): only set span.status=OK if UNSET
In modernized OpenTelemetry-Python, if the SpanStatus was not already set to OK, it can be changed and the code for trace_call was accidentally unconditionally setting the status to OK if there was no exception. This change fixes that and adds tests to lock this behavior in. Fixes googleapis#1246
1 parent eeb7836 commit f1d41ee

File tree

2 files changed

+45
-1
lines changed

2 files changed

+45
-1
lines changed

google/cloud/spanner_v1/_opentelemetry_tracing.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,10 @@ def trace_call(name, session, extra_attributes=None, observability_options=None)
105105
span.record_exception(error)
106106
raise
107107
else:
108-
span.set_status(Status(StatusCode.OK))
108+
if (not span._status) or span._status.status_code == StatusCode.UNSET:
109+
# OpenTelemetry-Python only allows a status change
110+
# if the current code is UNSET or ERROR. At the end
111+
# of the generator's consumption, only set it to OK
112+
# it wasn't previously set otherwise.
113+
# https://github.com/googleapis/python-spanner/issues/1246
114+
span.set_status(Status(StatusCode.OK))

tests/unit/test__opentelemetry_tracing.py

+38
Original file line numberDiff line numberDiff line change
@@ -158,3 +158,41 @@ def test_trace_codeless_error(self):
158158
self.assertEqual(len(span_list), 1)
159159
span = span_list[0]
160160
self.assertEqual(span.status.status_code, StatusCode.ERROR)
161+
162+
def test_trace_call_terminal_span_status(self):
163+
# Verify that we don't unconditionally set the terminal span status to
164+
# SpanStatus.OK per https://github.com/googleapis/python-spanner/issues/1246
165+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
166+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import (
167+
InMemorySpanExporter,
168+
)
169+
from opentelemetry.trace.status import Status, StatusCode
170+
from opentelemetry.sdk.trace import TracerProvider
171+
from opentelemetry.sdk.trace.sampling import ALWAYS_ON
172+
from opentelemetry import trace
173+
174+
tracer_provider = TracerProvider(sampler=ALWAYS_ON)
175+
trace_exporter = InMemorySpanExporter()
176+
tracer_provider.add_span_processor(SimpleSpanProcessor(trace_exporter))
177+
observability_options = dict(tracer_provider=tracer_provider)
178+
179+
session = _make_session()
180+
with _opentelemetry_tracing.trace_call(
181+
"VerifyTerminalSpanStatus",
182+
session,
183+
observability_options=observability_options,
184+
) as span:
185+
span.set_status(Status(StatusCode.ERROR, "Our error exhibit"))
186+
187+
span_list = trace_exporter.get_finished_spans()
188+
got_statuses = []
189+
190+
for span in span_list:
191+
got_statuses.append(
192+
(span.name, span.status.status_code, span.status.description)
193+
)
194+
195+
want_statuses = [
196+
("VerifyTerminalSpanStatus", StatusCode.ERROR, "Our error exhibit"),
197+
]
198+
assert got_statuses == want_statuses

0 commit comments

Comments
 (0)