Skip to content

Commit c6ae8e5

Browse files
authored
opentelemetry-exporter-richconsole: prevent deadlock (open-telemetry#3900)
* prevent deadlock * fix changelog * lint * add test timeout
1 parent 5ea0a71 commit c6ae8e5

File tree

4 files changed

+21
-3
lines changed

4 files changed

+21
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3030
([#3875](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3875))
3131
- `opentelemetry-instrumentation-aws-lambda`: Fix ImportError with slash-delimited handler paths
3232
([#3894](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3894))
33+
- `opentelemetry-exporter-richconsole`: Prevent deadlock when parent span is not part of the batch
34+
([#3900](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3900))
3335

3436
## Version 1.38.0/0.59b0 (2025-10-16)
3537

exporter/opentelemetry-exporter-richconsole/src/opentelemetry/exporter/richconsole/__init__.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,14 +173,17 @@ def spans_to_tree(spans: typing.Sequence[ReadableSpan]) -> Dict[str, Tree]:
173173
trees = {}
174174
parents = {}
175175
spans = list(spans)
176+
span_ids = {s.context.span_id for s in spans}
176177
while spans:
177178
for span in spans:
178-
if not span.parent:
179+
if not span.parent or span.parent.span_id not in span_ids:
179180
trace_id = opentelemetry.trace.format_trace_id(
180181
span.context.trace_id
181182
)
182-
trees[trace_id] = Tree(label=f"Trace {trace_id}")
183-
child = trees[trace_id].add(
183+
tree = trees.setdefault(
184+
trace_id, Tree(label=f"Trace {trace_id}")
185+
)
186+
child = tree.add(
184187
label=Text.from_markup(
185188
f"[blue][{_ns_to_time(span.start_time)}][/blue] [bold]{span.name}[/bold], span {opentelemetry.trace.format_span_id(span.context.span_id)}"
186189
)

exporter/opentelemetry-exporter-richconsole/test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pluggy==1.5.0
99
py-cpuinfo==9.0.0
1010
Pygments==2.17.2
1111
pytest==7.4.4
12+
pytest-timeout==2.3.1
1213
rich==13.7.1
1314
tomli==2.0.1
1415
typing_extensions==4.12.2

exporter/opentelemetry-exporter-richconsole/tests/test_rich_exporter.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,15 @@ def test_multiple_traces(tracer_provider):
9696
parent_2.name in child.label
9797
for child in trees[traceid_1].children[0].children
9898
)
99+
100+
101+
@pytest.mark.timeout(30)
102+
def test_no_deadlock(tracer_provider):
103+
# non-regression test for https://github.com/open-telemetry/opentelemetry-python-contrib/issues/3254
104+
105+
tracer = tracer_provider.get_tracer(__name__)
106+
with tracer.start_as_current_span("parent"):
107+
with tracer.start_as_current_span("child") as child:
108+
pass
109+
110+
RichConsoleSpanExporter.spans_to_tree((child,))

0 commit comments

Comments
 (0)