prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Track when an exception occurs in a Celery task
  Similar to how we do this in other libraries
  `tracked_request.tag("error", "true")`
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/scout_apm/celery.py`
  Content:
  ```
  1 # coding=utf-8
  2 from __future__ import absolute_import, division, print_function, unicode_literals
  3 
  4 import datetime as dt
  5 
  6 from celery.signals import before_task_publish, task_postrun, task_prerun
  7 
  8 import scout_apm.core
  9 from scout_apm.compat import datetime_to_timestamp
  10 from scout_apm.core.config import scout_config
  11 from scout_apm.core.tracked_request import TrackedRequest
  12 
  13 
  14 def before_task_publish_callback(headers=None, properties=None, **kwargs):
  15     if "scout_task_start" not in headers:
  16         headers["scout_task_start"] = datetime_to_timestamp(dt.datetime.utcnow())
  17 
  18 
  19 def task_prerun_callback(task=None, **kwargs):
  20     tracked_request = TrackedRequest.instance()
  21     tracked_request.is_real_request = True
  22 
  23     start = getattr(task.request, "scout_task_start", None)
  24     if start is not None:
  25         now = datetime_to_timestamp(dt.datetime.utcnow())
  26         try:
  27             queue_time = now - start
  28         except TypeError:
  29             pass
  30         else:
  31             tracked_request.tag("queue_time", queue_time)
  32 
  33     task_id = getattr(task.request, "id", None)
  34     if task_id:
  35         tracked_request.tag("task_id", task_id)
  36     parent_task_id = getattr(task.request, "parent_id", None)
  37     if parent_task_id:
  38         tracked_request.tag("parent_task_id", parent_task_id)
  39 
  40     delivery_info = task.request.delivery_info
  41     tracked_request.tag("is_eager", delivery_info.get("is_eager", False))
  42     tracked_request.tag("exchange", delivery_info.get("exchange", "unknown"))
  43     tracked_request.tag("priority", delivery_info.get("priority", "unknown"))
  44     tracked_request.tag("routing_key", delivery_info.get("routing_key", "unknown"))
  45     tracked_request.tag("queue", delivery_info.get("queue", "unknown"))
  46 
  47     tracked_request.start_span(operation=("Job/" + task.name))
  48 
  49 
  50 def task_postrun_callback(task=None, **kwargs):
  51     tracked_request = TrackedRequest.instance()
  52     tracked_request.stop_span()
  53 
  54 
  55 def install(app=None):
  56     if app is not None:
  57         copy_configuration(app)
  58 
  59     installed = scout_apm.core.install()
  60     if not installed:
  61         return
  62 
  63     before_task_publish.connect(before_task_publish_callback)
  64     task_prerun.connect(task_prerun_callback)
  65     task_postrun.connect(task_postrun_callback)
  66 
  67 
  68 def copy_configuration(app):
  69     prefix = "scout_"
  70     prefix_len = len(prefix)
  71 
  72     to_set = {}
  73     for key, value in app.conf.items():
  74         key_lower = key.lower()
  75         if key_lower.startswith(prefix) and len(key_lower) > prefix_len:
  76             scout_key = key_lower[prefix_len:]
  77             to_set[scout_key] = value
  78 
  79     scout_config.set(**to_set)
  80 
  81 
  82 def uninstall():
  83     before_task_publish.disconnect(before_task_publish_callback)
  84     task_prerun.disconnect(task_prerun_callback)
  85     task_postrun.disconnect(task_postrun_callback)
  86
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: 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
  @@ -3,7 +3,7 @@
   
   import datetime as dt
   
  -from celery.signals import before_task_publish, task_postrun, task_prerun
  +from celery.signals import before_task_publish, task_failure, task_postrun, task_prerun
   
   import scout_apm.core
   from scout_apm.compat import datetime_to_timestamp
  @@ -52,6 +52,11 @@
       tracked_request.stop_span()
   
   
  +def task_failure_callback(task_id=None, **kwargs):
  +    tracked_request = TrackedRequest.instance()
  +    tracked_request.tag("error", "true")
  +
  +
   def install(app=None):
       if app is not None:
           copy_configuration(app)
  @@ -62,6 +67,7 @@
   
       before_task_publish.connect(before_task_publish_callback)
       task_prerun.connect(task_prerun_callback)
  +    task_failure.connect(task_failure_callback)
       task_postrun.connect(task_postrun_callback)
  
verification_info: {"golden_diff": "diff --git a/src/scout_apm/celery.py b/src/scout_apm/celery.py\n--- a/src/scout_apm/celery.py\n+++ b/src/scout_apm/celery.py\n@@ -3,7 +3,7 @@\n \n import datetime as dt\n \n-from celery.signals import before_task_publish, task_postrun, task_prerun\n+from celery.signals import before_task_publish, task_failure, task_postrun, task_prerun\n \n import scout_apm.core\n from scout_apm.compat import datetime_to_timestamp\n@@ -52,6 +52,11 @@\n     tracked_request.stop_span()\n \n \n+def task_failure_callback(task_id=None, **kwargs):\n+    tracked_request = TrackedRequest.instance()\n+    tracked_request.tag(\"error\", \"true\")\n+\n+\n def install(app=None):\n     if app is not None:\n         copy_configuration(app)\n@@ -62,6 +67,7 @@\n \n     before_task_publish.connect(before_task_publish_callback)\n     task_prerun.connect(task_prerun_callback)\n+    task_failure.connect(task_failure_callback)\n     task_postrun.connect(task_postrun_callback)\n", "issue": "Track when an exception occurs in a Celery task\nSimilar to how we do this in other libraries\r\n`tracked_request.tag(\"error\", \"true\")`\r\n\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport datetime as dt\n\nfrom celery.signals import before_task_publish, task_postrun, task_prerun\n\nimport scout_apm.core\nfrom scout_apm.compat import datetime_to_timestamp\nfrom scout_apm.core.config import scout_config\nfrom scout_apm.core.tracked_request import TrackedRequest\n\n\ndef before_task_publish_callback(headers=None, properties=None, **kwargs):\n    if \"scout_task_start\" not in headers:\n        headers[\"scout_task_start\"] = datetime_to_timestamp(dt.datetime.utcnow())\n\n\ndef task_prerun_callback(task=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.is_real_request = True\n\n    start = getattr(task.request, \"scout_task_start\", None)\n    if start is not None:\n        now = datetime_to_timestamp(dt.datetime.utcnow())\n        try:\n            queue_time = now - start\n        except TypeError:\n            pass\n        else:\n            tracked_request.tag(\"queue_time\", queue_time)\n\n    task_id = getattr(task.request, \"id\", None)\n    if task_id:\n        tracked_request.tag(\"task_id\", task_id)\n    parent_task_id = getattr(task.request, \"parent_id\", None)\n    if parent_task_id:\n        tracked_request.tag(\"parent_task_id\", parent_task_id)\n\n    delivery_info = task.request.delivery_info\n    tracked_request.tag(\"is_eager\", delivery_info.get(\"is_eager\", False))\n    tracked_request.tag(\"exchange\", delivery_info.get(\"exchange\", \"unknown\"))\n    tracked_request.tag(\"priority\", delivery_info.get(\"priority\", \"unknown\"))\n    tracked_request.tag(\"routing_key\", delivery_info.get(\"routing_key\", \"unknown\"))\n    tracked_request.tag(\"queue\", delivery_info.get(\"queue\", \"unknown\"))\n\n    tracked_request.start_span(operation=(\"Job/\" + task.name))\n\n\ndef task_postrun_callback(task=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.stop_span()\n\n\ndef install(app=None):\n    if app is not None:\n        copy_configuration(app)\n\n    installed = scout_apm.core.install()\n    if not installed:\n        return\n\n    before_task_publish.connect(before_task_publish_callback)\n    task_prerun.connect(task_prerun_callback)\n    task_postrun.connect(task_postrun_callback)\n\n\ndef copy_configuration(app):\n    prefix = \"scout_\"\n    prefix_len = len(prefix)\n\n    to_set = {}\n    for key, value in app.conf.items():\n        key_lower = key.lower()\n        if key_lower.startswith(prefix) and len(key_lower) > prefix_len:\n            scout_key = key_lower[prefix_len:]\n            to_set[scout_key] = value\n\n    scout_config.set(**to_set)\n\n\ndef uninstall():\n    before_task_publish.disconnect(before_task_publish_callback)\n    task_prerun.disconnect(task_prerun_callback)\n    task_postrun.disconnect(task_postrun_callback)\n", "path": "src/scout_apm/celery.py"}], "after_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport datetime as dt\n\nfrom celery.signals import before_task_publish, task_failure, task_postrun, task_prerun\n\nimport scout_apm.core\nfrom scout_apm.compat import datetime_to_timestamp\nfrom scout_apm.core.config import scout_config\nfrom scout_apm.core.tracked_request import TrackedRequest\n\n\ndef before_task_publish_callback(headers=None, properties=None, **kwargs):\n    if \"scout_task_start\" not in headers:\n        headers[\"scout_task_start\"] = datetime_to_timestamp(dt.datetime.utcnow())\n\n\ndef task_prerun_callback(task=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.is_real_request = True\n\n    start = getattr(task.request, \"scout_task_start\", None)\n    if start is not None:\n        now = datetime_to_timestamp(dt.datetime.utcnow())\n        try:\n            queue_time = now - start\n        except TypeError:\n            pass\n        else:\n            tracked_request.tag(\"queue_time\", queue_time)\n\n    task_id = getattr(task.request, \"id\", None)\n    if task_id:\n        tracked_request.tag(\"task_id\", task_id)\n    parent_task_id = getattr(task.request, \"parent_id\", None)\n    if parent_task_id:\n        tracked_request.tag(\"parent_task_id\", parent_task_id)\n\n    delivery_info = task.request.delivery_info\n    tracked_request.tag(\"is_eager\", delivery_info.get(\"is_eager\", False))\n    tracked_request.tag(\"exchange\", delivery_info.get(\"exchange\", \"unknown\"))\n    tracked_request.tag(\"priority\", delivery_info.get(\"priority\", \"unknown\"))\n    tracked_request.tag(\"routing_key\", delivery_info.get(\"routing_key\", \"unknown\"))\n    tracked_request.tag(\"queue\", delivery_info.get(\"queue\", \"unknown\"))\n\n    tracked_request.start_span(operation=(\"Job/\" + task.name))\n\n\ndef task_postrun_callback(task=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.stop_span()\n\n\ndef task_failure_callback(task_id=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.tag(\"error\", \"true\")\n\n\ndef install(app=None):\n    if app is not None:\n        copy_configuration(app)\n\n    installed = scout_apm.core.install()\n    if not installed:\n        return\n\n    before_task_publish.connect(before_task_publish_callback)\n    task_prerun.connect(task_prerun_callback)\n    task_failure.connect(task_failure_callback)\n    task_postrun.connect(task_postrun_callback)\n\n\ndef copy_configuration(app):\n    prefix = \"scout_\"\n    prefix_len = len(prefix)\n\n    to_set = {}\n    for key, value in app.conf.items():\n        key_lower = key.lower()\n        if key_lower.startswith(prefix) and len(key_lower) > prefix_len:\n            scout_key = key_lower[prefix_len:]\n            to_set[scout_key] = value\n\n    scout_config.set(**to_set)\n\n\ndef uninstall():\n    before_task_publish.disconnect(before_task_publish_callback)\n    task_prerun.disconnect(task_prerun_callback)\n    task_postrun.disconnect(task_postrun_callback)\n", "path": "src/scout_apm/celery.py"}]}