Repository: scoutapp/scout_apm_python

Issue:
Capture Celery Queue Name



Discussion:

Resolving patch:
diff --git a/src/scout_apm/celery.py b/src/scout_apm/celery.py
--- a/src/scout_apm/celery.py
+++ b/src/scout_apm/celery.py
@@ -7,20 +7,22 @@
 from scout_apm.core.tracked_request import TrackedRequest
 
 
-# TODO: Capture queue.
-# https://stackoverflow.com/questions/22385297/how-to-get-the-queue-in-which-a-task-was-run-celery?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa
-def prerun_callback(sender=None, headers=None, body=None, **kwargs):
-    name = kwargs["task"].name
+def prerun_callback(task=None, **kwargs):
+    tracked_request = TrackedRequest.instance()
+    tracked_request.mark_real_request()
 
-    tr = TrackedRequest.instance()
-    tr.mark_real_request()
-    span = tr.start_span(operation=("Job/" + name))
-    span.tag("queue", "default")
+    delivery_info = task.request.delivery_info
+    tracked_request.tag("is_eager", delivery_info.get("is_eager", False))
+    tracked_request.tag("exchange", delivery_info.get("exchange", "unknown"))
+    tracked_request.tag("routing_key", delivery_info.get("routing_key", "unknown"))
+    tracked_request.tag("queue", delivery_info.get("queue", "unknown"))
 
+    tracked_request.start_span(operation=("Job/" + task.name))
 
-def postrun_callback(sender=None, headers=None, body=None, **kwargs):
-    tr = TrackedRequest.instance()
-    tr.stop_span()
+
+def postrun_callback(task=None, **kwargs):
+    tracked_request = TrackedRequest.instance()
+    tracked_request.stop_span()
 
 
 def install():


Test patch:
diff --git a/tests/integration/test_celery.py b/tests/integration/test_celery.py
--- a/tests/integration/test_celery.py
+++ b/tests/integration/test_celery.py
@@ -3,17 +3,35 @@
 
 from contextlib import contextmanager
 
-from celery import Celery
+import celery
+import pytest
+from celery.signals import setup_logging
 
 import scout_apm.celery
 from scout_apm.api import Config
 
+# http://docs.celeryproject.org/en/latest/userguide/testing.html#py-test
+skip_unless_celery_4_plus = pytest.mark.skipif(
+    celery.VERSION < (4, 0), reason="pytest fixtures added in Celery 4.0"
+)
+
+
+@setup_logging.connect
+def do_nothing(**kwargs):
+    # Just by connecting to this signal, we prevent Celery from setting up
+    # logging - and stop it from interfering with global state
+    # http://docs.celeryproject.org/en/v4.3.0/userguide/signals.html#setup-logging
+    pass
+
 
 @contextmanager
-def app_with_scout(config=None):
+def app_with_scout(app=None, config=None):
     """
     Context manager that configures a Celery app with Scout installed.
     """
+    if app is None:
+        app = celery.Celery("tasks", broker="memory://")
+
     # Enable Scout by default in tests.
     if config is None:
         config = {"monitor": True}
@@ -21,8 +39,6 @@ def app_with_scout(config=None):
     # Disable running the agent.
     config["core_agent_launch"] = False
 
-    app = Celery("tasks", broker="memory://")
-
     @app.task
     def hello():
         return "Hello World!"
@@ -39,23 +55,44 @@ def hello():
         Config.reset_all()
 
 
-def test_hello(tracked_requests):
+def test_hello_eager(tracked_requests):
     with app_with_scout() as app:
         result = app.tasks["tests.integration.test_celery.hello"].apply()
 
     assert result.result == "Hello World!"
     assert len(tracked_requests) == 1
     tracked_request = tracked_requests[0]
+    assert tracked_request.tags["is_eager"] is True
+    assert tracked_request.tags["exchange"] == "unknown"
+    assert tracked_request.tags["routing_key"] == "unknown"
+    assert tracked_request.tags["queue"] == "unknown"
+    assert tracked_request.active_spans == []
+    assert len(tracked_request.complete_spans) == 1
+    span = tracked_request.complete_spans[0]
+    assert span.operation == "Job/tests.integration.test_celery.hello"
+
+
+@skip_unless_celery_4_plus
+def test_hello_worker(celery_app, celery_worker, tracked_requests):
+    with app_with_scout(app=celery_app) as app:
+        result = app.tasks["tests.integration.test_celery.hello"].delay().get()
+
+    assert result == "Hello World!"
+    assert len(tracked_requests) == 1
+    tracked_request = tracked_requests[0]
+    assert tracked_request.tags["is_eager"] is False
+    assert tracked_request.tags["exchange"] == ""
+    assert tracked_request.tags["routing_key"] == "celery"
+    assert tracked_request.tags["queue"] == "unknown"
     assert tracked_request.active_spans == []
     assert len(tracked_request.complete_spans) == 1
     span = tracked_request.complete_spans[0]
     assert span.operation == "Job/tests.integration.test_celery.hello"
-    assert span.tags["queue"] == "default"
 
 
 def test_no_monitor(tracked_requests):
     # With an empty config, "monitor" defaults to False.
-    with app_with_scout({}) as app:
+    with app_with_scout(config={}) as app:
         result = app.tasks["tests.integration.test_celery.hello"].apply()
 
     assert result.result == "Hello World!"