Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tracing): do not raise exception if partial flush is triggered without any spans #9349

Merged
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
c1f25f2
chore(internal): log a warning if partial flushes has zero spans to send
romainkomorndatadog May 22, 2024
005a279
add test
romainkomorndatadog May 22, 2024
76271f6
relnote
romainkomorndatadog May 22, 2024
90d5346
force test to run with partial spans
romainkomorndatadog May 22, 2024
8bd0aaf
comment to force test failure
romainkomorndatadog May 22, 2024
e38af19
Update releasenotes/notes/fix-tracing-dont_raise_exception_on_empty_p…
romainkomorndatadog May 23, 2024
4c430ca
stash
romainkomorndatadog May 23, 2024
f2a9bc5
fix test
romainkomorndatadog May 23, 2024
c2d6fe0
Merge branch 'romain.komorn/AIT-10242/dont_crash_when_spans_list_empt…
romainkomorndatadog May 23, 2024
fbf212e
better log message
romainkomorndatadog May 23, 2024
ebb5ec0
test for missing trace, or missing span
romainkomorndatadog May 23, 2024
715ec1e
remove comment
romainkomorndatadog May 23, 2024
be16fb6
tiny tweaks
romainkomorndatadog May 23, 2024
3eddcce
move test and make ruff lint a little happier
romainkomorndatadog May 23, 2024
abc9e55
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog May 23, 2024
986e662
switch to reconfiguring span processor
romainkomorndatadog May 24, 2024
69a3e5a
update test
romainkomorndatadog May 24, 2024
5a5de55
Merge branch 'romain.komorn/AIT-10242/dont_crash_when_spans_list_empt…
romainkomorndatadog May 24, 2024
c992644
Revert "update test"
romainkomorndatadog May 24, 2024
13df4a9
Revert "switch to reconfiguring span processor"
romainkomorndatadog May 24, 2024
d2303af
revert back to just logging warnings
romainkomorndatadog May 24, 2024
f01664b
send one-span trace when a trace can't be found
romainkomorndatadog May 24, 2024
1554f4a
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog May 24, 2024
ce231a4
Apply suggestions from code review
brettlangdon May 24, 2024
6ec0335
update logic and test cases
brettlangdon May 24, 2024
a7c4a6b
Update tests/tracer/test_processors.py
brettlangdon May 24, 2024
94d3cd3
add additional partial flushing scenario
brettlangdon May 24, 2024
0871185
fix tests
brettlangdon May 24, 2024
677409c
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
brettlangdon May 24, 2024
13f14b6
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
brettlangdon May 24, 2024
b4e53b2
Apply suggestions from code review
brettlangdon May 28, 2024
93468d9
Apply suggestions from code review
brettlangdon May 29, 2024
afd47b6
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
brettlangdon May 29, 2024
3fd0f11
Ensure that lock is held, add tests
romainkomorndatadog Jun 11, 2024
0c021b9
Merge branch 'refs/heads/main' into romain.komorn/AIT-10242/dont_cras…
romainkomorndatadog Jun 11, 2024
94efd3b
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 11, 2024
ca31190
add utility method
romainkomorndatadog Jun 11, 2024
9021780
Merge branch 'romain.komorn/AIT-10242/dont_crash_when_spans_list_empt…
romainkomorndatadog Jun 11, 2024
4de8531
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
brettlangdon Jun 11, 2024
3c6c7d5
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 11, 2024
ae7bd63
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 11, 2024
8bce760
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 12, 2024
c909866
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 12, 2024
48051b0
Update ddtrace/_trace/processor/__init__.py
romainkomorndatadog Jun 12, 2024
43224b8
Update ddtrace/_trace/processor/__init__.py
romainkomorndatadog Jun 12, 2024
036db1f
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
emmettbutler Jun 12, 2024
4328b10
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
emmettbutler Jun 12, 2024
dbdde04
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
emmettbutler Jun 13, 2024
9f6ca75
Do not finish spans in os.fork child process
romainkomorndatadog Jun 14, 2024
24ba591
refactor test
romainkomorndatadog Jun 17, 2024
535d8c6
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 17, 2024
4f73a8a
comment out originally commented line
romainkomorndatadog Jun 17, 2024
0467e22
Merge branch 'romain.komorn/AIT-10242/dont_crash_when_spans_list_empt…
romainkomorndatadog Jun 17, 2024
d7434ef
revert os.fork instrumentation changes
romainkomorndatadog Jun 17, 2024
14c1cc4
revert changes to re-add trace on new traces found
romainkomorndatadog Jun 17, 2024
07649c9
return test to initial PR plan
romainkomorndatadog Jun 17, 2024
a5bb63b
remove duplicate test
romainkomorndatadog Jun 17, 2024
a86c5ad
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 17, 2024
5120910
switch logging levels to debug, telemetry to 'ERROR'
romainkomorndatadog Jun 17, 2024
1193055
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 17, 2024
a49062d
Merge branch 'main' into romain.komorn/AIT-10242/dont_crash_when_span…
romainkomorndatadog Jun 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 28 additions & 7 deletions ddtrace/_trace/processor/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,7 @@ class _Trace(object):
type=Dict[str, DefaultDict],
)

def on_span_start(self, span):
# type: (Span) -> None
def on_span_start(self, span: Span) -> None:
with self._lock:
trace = self._traces[span.trace_id]
trace.spans.append(span)
Expand All @@ -309,6 +308,17 @@ def on_span_finish(self, span):
# type: (Span) -> None
with self._lock:
self._span_metrics["spans_finished"][span._span_api] += 1

# Calling finish on a span that we did not see the start for
# DEV: This can occur if the SpanAggregator is recreated while there is a span in progress
# e.g. `tracer.configure()` is called after starting a span
if span.trace_id not in self._traces:
romainkomorndatadog marked this conversation as resolved.
Show resolved Hide resolved
log_msg = "finished span not connected to a trace"
if config._telemetry_enabled:
telemetry.telemetry_writer.add_log("WARN", log_msg)
log.warning("%s: %s", log_msg, span)
return

trace = self._traces[span.trace_id]
trace.num_finished += 1
should_partial_flush = self._partial_flush_enabled and trace.num_finished >= self._partial_flush_min_spans
Expand All @@ -326,16 +336,27 @@ def on_span_finish(self, span):
finished = trace_spans

num_finished = len(finished)
trace.num_finished -= num_finished
if trace.num_finished != 0:
log_msg = "unexpected finished span count"
if config._telemetry_enabled:
telemetry.telemetry_writer.add_log("WARN", log_msg)
log.warning("%s (%s) for span %s", log_msg, num_finished, span)
trace.num_finished = 0

# If we have removed all spans from this trace, then delete the trace from the traces dict
if len(trace.spans) == 0:
del self._traces[span.trace_id]

# No spans to process, return early
if not finished:
return

# Set partial flush tag on the first span
if should_partial_flush:
log.debug("Partially flushing %d spans for trace %d", num_finished, span.trace_id)
finished[0].set_metric("_dd.py.partial_flush", num_finished)

trace.num_finished -= num_finished

if len(trace.spans) == 0:
del self._traces[span.trace_id]

spans = finished # type: Optional[List[Span]]
for tp in self._trace_processors:
try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
fixes:
- |
tracing: fixes a potential crash where using partial flushes and ``tracer.configure()`` could result in an IndexError
8 changes: 7 additions & 1 deletion tests/contrib/subprocess/test_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,13 @@ def test_fork(tracer):
with tracer.trace("ossystem_test"):
pid = os.fork()
if pid == 0:
return
# Exit, otherwise the rest of this process will continue to be pytest
from ddtrace.contrib.coverage import unpatch

unpatch()
import pytest

pytest.exit("in forked child", returncode=0)

spans = tracer.pop()
assert spans
Expand Down
23 changes: 19 additions & 4 deletions tests/tracer/test_processors.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,9 +393,9 @@ def test_changing_tracer_sampler_changes_tracesamplingprocessor_sampler():
tracer = Tracer()
# get processor
for aggregator in tracer._deferred_processors:
if type(aggregator) == SpanAggregator:
if type(aggregator) is SpanAggregator:
for processor in aggregator._trace_processors:
if type(processor) == TraceSamplingProcessor:
if type(processor) is TraceSamplingProcessor:
sampling_processor = processor

assert sampling_processor.sampler is tracer._sampler
Expand Down Expand Up @@ -588,11 +588,11 @@ def assert_span_sampling_decision_tags(

def switch_out_trace_sampling_processor(tracer, sampling_processor):
for aggregator in tracer._deferred_processors:
if type(aggregator) == SpanAggregator:
if type(aggregator) is SpanAggregator:
i = 0
while i < len(aggregator._trace_processors):
processor = aggregator._trace_processors[i]
if type(processor) == TraceSamplingProcessor:
if type(processor) is TraceSamplingProcessor:
aggregator._trace_processors[i] = sampling_processor
break
i += 1
Expand Down Expand Up @@ -692,3 +692,18 @@ def on_span_finish(self, span):
with tracer.trace("test") as span:
assert span.get_tag("on_start") is None
assert span.get_tag("on_finish") is None


def _stderr_contains_log(stderr: str) -> bool:
return "finished span not connected to a trace" in stderr


@pytest.mark.subprocess(err=_stderr_contains_log)
def test_tracer_reconfigured_with_active_span_does_not_crash():
import ddtrace

with ddtrace.tracer.trace("regression1") as exploding_span:
# Reconfiguring the tracer clears active traces
# Calling .finish() manually bypasses the code that catches the exception
ddtrace.tracer.configure(partial_flush_enabled=True, partial_flush_min_spans=1)
exploding_span.finish()
Loading