|
| 1 | +import os |
| 2 | +import re |
| 3 | +import subprocess |
| 4 | +import time |
| 5 | + |
| 6 | +from celery import Celery |
| 7 | + |
| 8 | + |
| 9 | +# Ensure that when we call Celery chains, the root span has celery specific span tags |
| 10 | +# The test_integration.py setup doesn't perfectly mimic the condition of a worker process running. |
| 11 | +# This test runs the worker as a side so we can check the tracer logs afterwards to ensure expected span results. |
| 12 | +# See https://github.com/DataDog/dd-trace-py/issues/11479 |
| 13 | +def test_task_chain_task_call_task(): |
| 14 | + app = Celery("tasks") |
| 15 | + |
| 16 | + celery_worker_cmd = "ddtrace-run celery -A tasks worker -c 1 -l DEBUG -n uniquename1 -P solo" |
| 17 | + celery_task_runner_cmd = "ddtrace-run python run_tasks.py" |
| 18 | + |
| 19 | + # The commands need to run from the directory where this test file lives |
| 20 | + current_directory = str(os.path.dirname(__file__)) |
| 21 | + |
| 22 | + worker_process = subprocess.Popen( |
| 23 | + celery_worker_cmd.split(), |
| 24 | + stdout=subprocess.PIPE, |
| 25 | + stderr=subprocess.PIPE, |
| 26 | + preexec_fn=os.setsid, |
| 27 | + close_fds=True, |
| 28 | + cwd=current_directory, |
| 29 | + ) |
| 30 | + |
| 31 | + max_wait_time = 10 |
| 32 | + waited_so_far = 0 |
| 33 | + # {app.control.inspect().active() returns {'celery@uniquename1': []} when the worker is running} |
| 34 | + while app.control.inspect().active() is None and waited_so_far < max_wait_time: |
| 35 | + time.sleep(1) |
| 36 | + waited_so_far += 1 |
| 37 | + |
| 38 | + # The task should only run after the Celery worker has sufficient time to start up |
| 39 | + task_runner_process = subprocess.Popen( |
| 40 | + celery_task_runner_cmd.split(), |
| 41 | + stdout=subprocess.PIPE, |
| 42 | + stderr=subprocess.PIPE, |
| 43 | + preexec_fn=os.setsid, |
| 44 | + close_fds=True, |
| 45 | + cwd=current_directory, |
| 46 | + ) |
| 47 | + |
| 48 | + task_runner_process.wait() |
| 49 | + # Kill the process so it starts to send traces to the Trace Agent |
| 50 | + worker_process.kill() |
| 51 | + worker_logs = worker_process.stderr.read() |
| 52 | + |
| 53 | + # Check that the root span was created with one of the Celery specific tags, such as celery.correlation_id |
| 54 | + # Some versions of python seem to require escaping when using `re.search`: |
| 55 | + old_pattern_match = r"resource=\\'tests.contrib.celery.tasks.fn_a\\' type=\\'worker\\' .* tags=.*correlation_id.*" |
| 56 | + new_pattern_match = r"resource=\'tests.contrib.celery.tasks.fn_a\' type=\'worker\' .* tags=.*correlation_id.*" |
| 57 | + |
| 58 | + pattern_exists = ( |
| 59 | + re.search(old_pattern_match, str(worker_logs)) is not None |
| 60 | + or re.search(new_pattern_match, str(worker_logs)) is not None |
| 61 | + ) |
| 62 | + assert pattern_exists is not None |
0 commit comments