We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Require Celery app reference to read configuration
We had a customer whose Celery tasks weren't reporting whilst their Django views were. It turns out they had configured in the Django settings file, which isn't applied when Celery runs. This is because it doesn't run "under" Django through `manage.py`, but separately through `celery worker`.

The django pattern is to use [Celery's `app.config_from_object`](https://docs.celeryproject.org/en/latest/reference/celery.html#celery.Celery.config_from_object) to read the Django settings. If we then read out of there for the scout settings, we would again allow shared configuration between the two.

This would need changing the Celery install process to take an `app` argument:

```python
app = celery.Celery(..)
...
scout_apm.celery.install(app)
```

We should work without this for backwards compatibility reasons, but throw a warninng when it's not passed as I predict this issue will appear repeatedly if we don't encourage users this way.

--- 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.tracked_request import TrackedRequest
11 
12 
13 def before_publish_callback(headers=None, properties=None, **kwargs):
14     if "scout_task_start" not in headers:
15         headers["scout_task_start"] = datetime_to_timestamp(dt.datetime.utcnow())
16 
17 
18 def prerun_callback(task=None, **kwargs):
19     tracked_request = TrackedRequest.instance()
20     tracked_request.is_real_request = True
21 
22     start = getattr(task.request, "scout_task_start", None)
23     if start is not None:
24         now = datetime_to_timestamp(dt.datetime.utcnow())
25         try:
26             queue_time = now - start
27         except TypeError:
28             pass
29         else:
30             tracked_request.tag("queue_time", queue_time)
31 
32     task_id = getattr(task.request, "id", None)
33     if task_id:
34         tracked_request.tag("task_id", task_id)
35     parent_task_id = getattr(task.request, "parent_id", None)
36     if parent_task_id:
37         tracked_request.tag("parent_task_id", parent_task_id)
38 
39     delivery_info = task.request.delivery_info
40     tracked_request.tag("is_eager", delivery_info.get("is_eager", False))
41     tracked_request.tag("exchange", delivery_info.get("exchange", "unknown"))
42     tracked_request.tag("routing_key", delivery_info.get("routing_key", "unknown"))
43     tracked_request.tag("queue", delivery_info.get("queue", "unknown"))
44 
45     tracked_request.start_span(operation=("Job/" + task.name))
46 
47 
48 def postrun_callback(task=None, **kwargs):
49     tracked_request = TrackedRequest.instance()
50     tracked_request.stop_span()
51 
52 
53 def install():
54     installed = scout_apm.core.install()
55     if not installed:
56         return
57 
58     before_task_publish.connect(before_publish_callback)
59     task_prerun.connect(prerun_callback)
60     task_postrun.connect(postrun_callback)
61 
62 
63 def uninstall():
64     before_task_publish.disconnect(before_publish_callback)
65     task_prerun.disconnect(prerun_callback)
66     task_postrun.disconnect(postrun_callback)
67
```
--- 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()

```

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,15 +7,16 @@
 
 import scout_apm.core
 from scout_apm.compat import datetime_to_timestamp
+from scout_apm.core.config import scout_config
 from scout_apm.core.tracked_request import TrackedRequest
 
 
-def before_publish_callback(headers=None, properties=None, **kwargs):
+def before_task_publish_callback(headers=None, properties=None, **kwargs):
     if "scout_task_start" not in headers:
         headers["scout_task_start"] = datetime_to_timestamp(dt.datetime.utcnow())
 
 
-def prerun_callback(task=None, **kwargs):
+def task_prerun_callback(task=None, **kwargs):
     tracked_request = TrackedRequest.instance()
     tracked_request.is_real_request = True
 
@@ -45,22 +46,39 @@
     tracked_request.start_span(operation=("Job/" + task.name))
 
 
-def postrun_callback(task=None, **kwargs):
+def task_postrun_callback(task=None, **kwargs):
     tracked_request = TrackedRequest.instance()
     tracked_request.stop_span()
 
 
-def install():
+def install(app=None):
+    if app is not None:
+        copy_configuration(app)
+
     installed = scout_apm.core.install()
     if not installed:
         return
 
-    before_task_publish.connect(before_publish_callback)
-    task_prerun.connect(prerun_callback)
-    task_postrun.connect(postrun_callback)
+    before_task_publish.connect(before_task_publish_callback)
+    task_prerun.connect(task_prerun_callback)
+    task_postrun.connect(task_postrun_callback)
+
+
+def copy_configuration(app):
+    prefix = "scout_"
+    prefix_len = len(prefix)
+
+    to_set = {}
+    for key, value in app.conf.items():
+        key_lower = key.lower()
+        if key_lower.startswith(prefix) and len(key_lower) > prefix_len:
+            scout_key = key_lower[prefix_len:]
+            to_set[scout_key] = value
+
+    scout_config.set(**to_set)
 
 
 def uninstall():
-    before_task_publish.disconnect(before_publish_callback)
-    task_prerun.disconnect(prerun_callback)
-    task_postrun.disconnect(postrun_callback)
+    before_task_publish.disconnect(before_task_publish_callback)
+    task_prerun.disconnect(task_prerun_callback)
+    task_postrun.disconnect(task_postrun_callback)

{"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@@ -7,15 +7,16 @@\n \n import scout_apm.core\n from scout_apm.compat import datetime_to_timestamp\n+from scout_apm.core.config import scout_config\n from scout_apm.core.tracked_request import TrackedRequest\n \n \n-def before_publish_callback(headers=None, properties=None, **kwargs):\n+def 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 \n-def prerun_callback(task=None, **kwargs):\n+def task_prerun_callback(task=None, **kwargs):\n     tracked_request = TrackedRequest.instance()\n     tracked_request.is_real_request = True\n \n@@ -45,22 +46,39 @@\n     tracked_request.start_span(operation=(\"Job/\" + task.name))\n \n \n-def postrun_callback(task=None, **kwargs):\n+def task_postrun_callback(task=None, **kwargs):\n     tracked_request = TrackedRequest.instance()\n     tracked_request.stop_span()\n \n \n-def install():\n+def 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_publish_callback)\n-    task_prerun.connect(prerun_callback)\n-    task_postrun.connect(postrun_callback)\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+\n+def 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 \n def uninstall():\n-    before_task_publish.disconnect(before_publish_callback)\n-    task_prerun.disconnect(prerun_callback)\n-    task_postrun.disconnect(postrun_callback)\n+    before_task_publish.disconnect(before_task_publish_callback)\n+    task_prerun.disconnect(task_prerun_callback)\n+    task_postrun.disconnect(task_postrun_callback)\n", "issue": "Require Celery app reference to read configuration\nWe had a customer whose Celery tasks weren't reporting whilst their Django views were. It turns out they had configured in the Django settings file, which isn't applied when Celery runs. This is because it doesn't run \"under\" Django through `manage.py`, but separately through `celery worker`.\r\n\r\nThe django pattern is to use [Celery's `app.config_from_object`](https://docs.celeryproject.org/en/latest/reference/celery.html#celery.Celery.config_from_object) to read the Django settings. If we then read out of there for the scout settings, we would again allow shared configuration between the two.\r\n\r\nThis would need changing the Celery install process to take an `app` argument:\r\n\r\n```python\r\napp = celery.Celery(..)\r\n...\r\nscout_apm.celery.install(app)\r\n```\r\n\r\nWe should work without this for backwards compatibility reasons, but throw a warninng when it's not passed as I predict this issue will appear repeatedly if we don't encourage users this way.\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.tracked_request import TrackedRequest\n\n\ndef before_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 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(\"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 postrun_callback(task=None, **kwargs):\n    tracked_request = TrackedRequest.instance()\n    tracked_request.stop_span()\n\n\ndef install():\n    installed = scout_apm.core.install()\n    if not installed:\n        return\n\n    before_task_publish.connect(before_publish_callback)\n    task_prerun.connect(prerun_callback)\n    task_postrun.connect(postrun_callback)\n\n\ndef uninstall():\n    before_task_publish.disconnect(before_publish_callback)\n    task_prerun.disconnect(prerun_callback)\n    task_postrun.disconnect(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_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(\"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"}]}