We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Instrument Starlette background tasks
Starlette supports [background tasks](https://www.starlette.io/background/). We should instrument these as background transactions.

--- 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/async_/starlette.py`
Content:
```
1 # coding=utf-8
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 
4 from starlette.requests import Request
5 
6 import scout_apm.core
7 from scout_apm.core.tracked_request import TrackedRequest
8 from scout_apm.core.web_requests import (
9     create_filtered_path,
10     ignore_path,
11     track_amazon_request_queue_time,
12     track_request_queue_time,
13 )
14 
15 
16 class ScoutMiddleware:
17     def __init__(self, app):
18         self.app = app
19         installed = scout_apm.core.install()
20         self._do_nothing = not installed
21 
22     async def __call__(self, scope, receive, send):
23         if self._do_nothing or scope["type"] != "http":
24             await self.app(scope, receive, send)
25             return
26 
27         request = Request(scope)
28         tracked_request = TrackedRequest.instance()
29         # Can't name controller until post-routing - see final clause
30         controller_span = tracked_request.start_span(operation="Controller/Unknown")
31 
32         tracked_request.tag(
33             "path",
34             create_filtered_path(request.url.path, request.query_params.multi_items()),
35         )
36         if ignore_path(request.url.path):
37             tracked_request.tag("ignore_transaction", True)
38 
39         user_ip = (
40             request.headers.get("x-forwarded-for", default="").split(",")[0]
41             or request.headers.get("client-ip", default="").split(",")[0]
42             or request.client.host
43         )
44         tracked_request.tag("user_ip", user_ip)
45 
46         queue_time = request.headers.get(
47             "x-queue-start", default=""
48         ) or request.headers.get("x-request-start", default="")
49         tracked_queue_time = track_request_queue_time(queue_time, tracked_request)
50         if not tracked_queue_time:
51             amazon_queue_time = request.headers.get("x-amzn-trace-id", default="")
52             track_amazon_request_queue_time(amazon_queue_time, tracked_request)
53 
54         try:
55             await self.app(scope, receive, send)
56         except Exception as exc:
57             tracked_request.tag("error", "true")
58             raise exc
59         finally:
60             if "endpoint" in scope:
61                 endpoint = scope["endpoint"]
62                 controller_span.operation = "Controller/{}.{}".format(
63                     endpoint.__module__, endpoint.__qualname__
64                 )
65                 tracked_request.is_real_request = True
66             tracked_request.stop_span()
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/async_/starlette.py b/src/scout_apm/async_/starlette.py
--- a/src/scout_apm/async_/starlette.py
+++ b/src/scout_apm/async_/starlette.py
@@ -1,6 +1,8 @@
 # coding=utf-8
 from __future__ import absolute_import, division, print_function, unicode_literals
 
+import wrapt
+from starlette.background import BackgroundTask
 from starlette.requests import Request
 
 import scout_apm.core
@@ -18,6 +20,8 @@
         self.app = app
         installed = scout_apm.core.install()
         self._do_nothing = not installed
+        if installed:
+            install_background_instrumentation()
 
     async def __call__(self, scope, receive, send):
         if self._do_nothing or scope["type"] != "http":
@@ -51,16 +55,57 @@
             amazon_queue_time = request.headers.get("x-amzn-trace-id", default="")
             track_amazon_request_queue_time(amazon_queue_time, tracked_request)
 
-        try:
-            await self.app(scope, receive, send)
-        except Exception as exc:
-            tracked_request.tag("error", "true")
-            raise exc
-        finally:
+        def rename_controller_span_from_endpoint():
             if "endpoint" in scope:
+                # Rename top span
                 endpoint = scope["endpoint"]
                 controller_span.operation = "Controller/{}.{}".format(
                     endpoint.__module__, endpoint.__qualname__
                 )
                 tracked_request.is_real_request = True
+
+        async def wrapped_send(data):
+            # Finish HTTP span when body finishes sending, not later (e.g.
+            # after background tasks)
+            if data.get("type", None) == "http.response.body" and not data.get(
+                "more_body", False
+            ):
+                rename_controller_span_from_endpoint()
+                tracked_request.stop_span()
+            return await send(data)
+
+        try:
+            await self.app(scope, receive, wrapped_send)
+        except Exception as exc:
+            tracked_request.tag("error", "true")
+            raise exc
+        finally:
+            if tracked_request.end_time is None:
+                rename_controller_span_from_endpoint()
+                tracked_request.stop_span()
+
+
+background_instrumentation_installed = False
+
+
+def install_background_instrumentation():
+    global background_instrumentation_installed
+    if background_instrumentation_installed:
+        return
+    background_instrumentation_installed = True
+
+    @wrapt.decorator
+    async def wrapped_background_call(wrapped, instance, args, kwargs):
+        tracked_request = TrackedRequest.instance()
+        tracked_request.is_real_request = True
+        tracked_request.start_span(
+            operation="Job/{}.{}".format(
+                instance.func.__module__, instance.func.__qualname__
+            )
+        )
+        try:
+            return await wrapped(*args, **kwargs)
+        finally:
             tracked_request.stop_span()
+
+    BackgroundTask.__call__ = wrapped_background_call(BackgroundTask.__call__)

{"golden_diff": "diff --git a/src/scout_apm/async_/starlette.py b/src/scout_apm/async_/starlette.py\n--- a/src/scout_apm/async_/starlette.py\n+++ b/src/scout_apm/async_/starlette.py\n@@ -1,6 +1,8 @@\n # coding=utf-8\n from __future__ import absolute_import, division, print_function, unicode_literals\n \n+import wrapt\n+from starlette.background import BackgroundTask\n from starlette.requests import Request\n \n import scout_apm.core\n@@ -18,6 +20,8 @@\n         self.app = app\n         installed = scout_apm.core.install()\n         self._do_nothing = not installed\n+        if installed:\n+            install_background_instrumentation()\n \n     async def __call__(self, scope, receive, send):\n         if self._do_nothing or scope[\"type\"] != \"http\":\n@@ -51,16 +55,57 @@\n             amazon_queue_time = request.headers.get(\"x-amzn-trace-id\", default=\"\")\n             track_amazon_request_queue_time(amazon_queue_time, tracked_request)\n \n-        try:\n-            await self.app(scope, receive, send)\n-        except Exception as exc:\n-            tracked_request.tag(\"error\", \"true\")\n-            raise exc\n-        finally:\n+        def rename_controller_span_from_endpoint():\n             if \"endpoint\" in scope:\n+                # Rename top span\n                 endpoint = scope[\"endpoint\"]\n                 controller_span.operation = \"Controller/{}.{}\".format(\n                     endpoint.__module__, endpoint.__qualname__\n                 )\n                 tracked_request.is_real_request = True\n+\n+        async def wrapped_send(data):\n+            # Finish HTTP span when body finishes sending, not later (e.g.\n+            # after background tasks)\n+            if data.get(\"type\", None) == \"http.response.body\" and not data.get(\n+                \"more_body\", False\n+            ):\n+                rename_controller_span_from_endpoint()\n+                tracked_request.stop_span()\n+            return await send(data)\n+\n+        try:\n+            await self.app(scope, receive, wrapped_send)\n+        except Exception as exc:\n+            tracked_request.tag(\"error\", \"true\")\n+            raise exc\n+        finally:\n+            if tracked_request.end_time is None:\n+                rename_controller_span_from_endpoint()\n+                tracked_request.stop_span()\n+\n+\n+background_instrumentation_installed = False\n+\n+\n+def install_background_instrumentation():\n+    global background_instrumentation_installed\n+    if background_instrumentation_installed:\n+        return\n+    background_instrumentation_installed = True\n+\n+    @wrapt.decorator\n+    async def wrapped_background_call(wrapped, instance, args, kwargs):\n+        tracked_request = TrackedRequest.instance()\n+        tracked_request.is_real_request = True\n+        tracked_request.start_span(\n+            operation=\"Job/{}.{}\".format(\n+                instance.func.__module__, instance.func.__qualname__\n+            )\n+        )\n+        try:\n+            return await wrapped(*args, **kwargs)\n+        finally:\n             tracked_request.stop_span()\n+\n+    BackgroundTask.__call__ = wrapped_background_call(BackgroundTask.__call__)\n", "issue": "Instrument Starlette background tasks\nStarlette supports [background tasks](https://www.starlette.io/background/). We should instrument these as background transactions.\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom starlette.requests import Request\n\nimport scout_apm.core\nfrom scout_apm.core.tracked_request import TrackedRequest\nfrom scout_apm.core.web_requests import (\n    create_filtered_path,\n    ignore_path,\n    track_amazon_request_queue_time,\n    track_request_queue_time,\n)\n\n\nclass ScoutMiddleware:\n    def __init__(self, app):\n        self.app = app\n        installed = scout_apm.core.install()\n        self._do_nothing = not installed\n\n    async def __call__(self, scope, receive, send):\n        if self._do_nothing or scope[\"type\"] != \"http\":\n            await self.app(scope, receive, send)\n            return\n\n        request = Request(scope)\n        tracked_request = TrackedRequest.instance()\n        # Can't name controller until post-routing - see final clause\n        controller_span = tracked_request.start_span(operation=\"Controller/Unknown\")\n\n        tracked_request.tag(\n            \"path\",\n            create_filtered_path(request.url.path, request.query_params.multi_items()),\n        )\n        if ignore_path(request.url.path):\n            tracked_request.tag(\"ignore_transaction\", True)\n\n        user_ip = (\n            request.headers.get(\"x-forwarded-for\", default=\"\").split(\",\")[0]\n            or request.headers.get(\"client-ip\", default=\"\").split(\",\")[0]\n            or request.client.host\n        )\n        tracked_request.tag(\"user_ip\", user_ip)\n\n        queue_time = request.headers.get(\n            \"x-queue-start\", default=\"\"\n        ) or request.headers.get(\"x-request-start\", default=\"\")\n        tracked_queue_time = track_request_queue_time(queue_time, tracked_request)\n        if not tracked_queue_time:\n            amazon_queue_time = request.headers.get(\"x-amzn-trace-id\", default=\"\")\n            track_amazon_request_queue_time(amazon_queue_time, tracked_request)\n\n        try:\n            await self.app(scope, receive, send)\n        except Exception as exc:\n            tracked_request.tag(\"error\", \"true\")\n            raise exc\n        finally:\n            if \"endpoint\" in scope:\n                endpoint = scope[\"endpoint\"]\n                controller_span.operation = \"Controller/{}.{}\".format(\n                    endpoint.__module__, endpoint.__qualname__\n                )\n                tracked_request.is_real_request = True\n            tracked_request.stop_span()\n", "path": "src/scout_apm/async_/starlette.py"}], "after_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport wrapt\nfrom starlette.background import BackgroundTask\nfrom starlette.requests import Request\n\nimport scout_apm.core\nfrom scout_apm.core.tracked_request import TrackedRequest\nfrom scout_apm.core.web_requests import (\n    create_filtered_path,\n    ignore_path,\n    track_amazon_request_queue_time,\n    track_request_queue_time,\n)\n\n\nclass ScoutMiddleware:\n    def __init__(self, app):\n        self.app = app\n        installed = scout_apm.core.install()\n        self._do_nothing = not installed\n        if installed:\n            install_background_instrumentation()\n\n    async def __call__(self, scope, receive, send):\n        if self._do_nothing or scope[\"type\"] != \"http\":\n            await self.app(scope, receive, send)\n            return\n\n        request = Request(scope)\n        tracked_request = TrackedRequest.instance()\n        # Can't name controller until post-routing - see final clause\n        controller_span = tracked_request.start_span(operation=\"Controller/Unknown\")\n\n        tracked_request.tag(\n            \"path\",\n            create_filtered_path(request.url.path, request.query_params.multi_items()),\n        )\n        if ignore_path(request.url.path):\n            tracked_request.tag(\"ignore_transaction\", True)\n\n        user_ip = (\n            request.headers.get(\"x-forwarded-for\", default=\"\").split(\",\")[0]\n            or request.headers.get(\"client-ip\", default=\"\").split(\",\")[0]\n            or request.client.host\n        )\n        tracked_request.tag(\"user_ip\", user_ip)\n\n        queue_time = request.headers.get(\n            \"x-queue-start\", default=\"\"\n        ) or request.headers.get(\"x-request-start\", default=\"\")\n        tracked_queue_time = track_request_queue_time(queue_time, tracked_request)\n        if not tracked_queue_time:\n            amazon_queue_time = request.headers.get(\"x-amzn-trace-id\", default=\"\")\n            track_amazon_request_queue_time(amazon_queue_time, tracked_request)\n\n        def rename_controller_span_from_endpoint():\n            if \"endpoint\" in scope:\n                # Rename top span\n                endpoint = scope[\"endpoint\"]\n                controller_span.operation = \"Controller/{}.{}\".format(\n                    endpoint.__module__, endpoint.__qualname__\n                )\n                tracked_request.is_real_request = True\n\n        async def wrapped_send(data):\n            # Finish HTTP span when body finishes sending, not later (e.g.\n            # after background tasks)\n            if data.get(\"type\", None) == \"http.response.body\" and not data.get(\n                \"more_body\", False\n            ):\n                rename_controller_span_from_endpoint()\n                tracked_request.stop_span()\n            return await send(data)\n\n        try:\n            await self.app(scope, receive, wrapped_send)\n        except Exception as exc:\n            tracked_request.tag(\"error\", \"true\")\n            raise exc\n        finally:\n            if tracked_request.end_time is None:\n                rename_controller_span_from_endpoint()\n                tracked_request.stop_span()\n\n\nbackground_instrumentation_installed = False\n\n\ndef install_background_instrumentation():\n    global background_instrumentation_installed\n    if background_instrumentation_installed:\n        return\n    background_instrumentation_installed = True\n\n    @wrapt.decorator\n    async def wrapped_background_call(wrapped, instance, args, kwargs):\n        tracked_request = TrackedRequest.instance()\n        tracked_request.is_real_request = True\n        tracked_request.start_span(\n            operation=\"Job/{}.{}\".format(\n                instance.func.__module__, instance.func.__qualname__\n            )\n        )\n        try:\n            return await wrapped(*args, **kwargs)\n        finally:\n            tracked_request.stop_span()\n\n    BackgroundTask.__call__ = wrapped_background_call(BackgroundTask.__call__)\n", "path": "src/scout_apm/async_/starlette.py"}]}