prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Instrument non-index Elasticsearch client methods
  There are many methods on the `elasticsearch-py` client class that we aren't instrumenting, for example `ping()`, because they don't operate on an index.
  
  We should capture all these calls too - I'm sure many applications have such calls that would be good to show on traces.
  
  https://github.com/elastic/elasticsearch-py/blob/master/elasticsearch/client/__init__.py
  
  --- 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/instruments/elasticsearch.py`
  Content:
  ```
  1 # coding=utf-8
  2 from __future__ import absolute_import, division, print_function, unicode_literals
  3 
  4 import logging
  5 
  6 import wrapt
  7 
  8 from scout_apm.core.tracked_request import TrackedRequest
  9 
  10 try:
  11     from elasticsearch import Elasticsearch, Transport
  12 except ImportError:  # pragma: no cover
  13     Elasticsearch = None
  14     Transport = None
  15 
  16 logger = logging.getLogger(__name__)
  17 
  18 
  19 def ensure_installed():
  20     logger.info("Ensuring elasticsearch instrumentation is installed.")
  21 
  22     if Elasticsearch is None:
  23         logger.info("Unable to import elasticsearch.Elasticsearch")
  24     else:
  25         ensure_client_instrumented()
  26         ensure_transport_instrumented()
  27 
  28 
  29 CLIENT_METHODS = [
  30     "bulk",
  31     "count",
  32     "create",
  33     "delete",
  34     "delete_by_query",
  35     "exists",
  36     "exists_source",
  37     "explain",
  38     "field_caps",
  39     "get",
  40     "get_source",
  41     "index",
  42     "mget",
  43     "msearch",
  44     "msearch_template",
  45     "mtermvectors",
  46     "reindex",
  47     "reindex_rethrottle",
  48     "search",
  49     "search_shards",
  50     "search_template",
  51     "termvectors",
  52     "update",
  53     "update_by_query",
  54 ]
  55 
  56 
  57 have_patched_client = False
  58 
  59 
  60 def ensure_client_instrumented():
  61     global have_patched_client
  62 
  63     if not have_patched_client:
  64         for name in CLIENT_METHODS:
  65             try:
  66                 setattr(
  67                     Elasticsearch,
  68                     name,
  69                     wrap_client_method(getattr(Elasticsearch, name)),
  70                 )
  71             except Exception as exc:
  72                 logger.warning(
  73                     "Unable to instrument elasticsearch.Elasticsearch.%s: %r",
  74                     name,
  75                     exc,
  76                     exc_info=exc,
  77                 )
  78 
  79         have_patched_client = True
  80 
  81 
  82 @wrapt.decorator
  83 def wrap_client_method(wrapped, instance, args, kwargs):
  84     def _get_index(index, *args, **kwargs):
  85         return index
  86 
  87     try:
  88         index = _get_index(*args, **kwargs)
  89     except TypeError:
  90         index = "Unknown"
  91     else:
  92         if not index:
  93             index = "Unknown"
  94         if isinstance(index, (list, tuple)):
  95             index = ",".join(index)
  96     index = index.title()
  97     camel_name = "".join(c.title() for c in wrapped.__name__.split("_"))
  98     operation = "Elasticsearch/{}/{}".format(index, camel_name)
  99     tracked_request = TrackedRequest.instance()
  100     tracked_request.start_span(operation=operation, ignore_children=True)
  101 
  102     try:
  103         return wrapped(*args, **kwargs)
  104     finally:
  105         tracked_request.stop_span()
  106 
  107 
  108 have_patched_transport = False
  109 
  110 
  111 def ensure_transport_instrumented():
  112     global have_patched_transport
  113 
  114     if not have_patched_transport:
  115         try:
  116             Transport.perform_request = wrapped_perform_request(
  117                 Transport.perform_request
  118             )
  119         except Exception as exc:
  120             logger.warning(
  121                 "Unable to instrument elasticsearch.Transport.perform_request: %r",
  122                 exc,
  123                 exc_info=exc,
  124             )
  125 
  126     have_patched_transport = True
  127 
  128 
  129 def _sanitize_name(name):
  130     try:
  131         op = name.split("/")[-1]
  132         op = op[1:]  # chop leading '_' from op
  133         known_names = (
  134             "bench",
  135             "bulk",
  136             "count",
  137             "exists",
  138             "explain",
  139             "field_stats",
  140             "health",
  141             "mget",
  142             "mlt",
  143             "mpercolate",
  144             "msearch",
  145             "mtermvectors",
  146             "percolate",
  147             "query",
  148             "scroll",
  149             "search_shards",
  150             "source",
  151             "suggest",
  152             "template",
  153             "termvectors",
  154             "update",
  155             "search",
  156         )
  157         if op in known_names:
  158             return op.title()
  159         return "Unknown"
  160     except Exception:
  161         return "Unknown"
  162 
  163 
  164 @wrapt.decorator
  165 def wrapped_perform_request(wrapped, instance, args, kwargs):
  166     try:
  167         op = _sanitize_name(args[1])
  168     except IndexError:
  169         op = "Unknown"
  170 
  171     tracked_request = TrackedRequest.instance()
  172     tracked_request.start_span(
  173         operation="Elasticsearch/{}".format(op), ignore_children=True
  174     )
  175 
  176     try:
  177         return wrapped(*args, **kwargs)
  178     finally:
  179         tracked_request.stop_span()
  180
  ```
  --- 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/instruments/elasticsearch.py b/src/scout_apm/instruments/elasticsearch.py
  --- a/src/scout_apm/instruments/elasticsearch.py
  +++ b/src/scout_apm/instruments/elasticsearch.py
  @@ -2,6 +2,7 @@
   from __future__ import absolute_import, division, print_function, unicode_literals
   
   import logging
  +from collections import namedtuple
   
   import wrapt
   
  @@ -26,31 +27,46 @@
           ensure_transport_instrumented()
   
   
  +ClientMethod = namedtuple("ClientMethod", ["name", "takes_index_argument"])
  +
   CLIENT_METHODS = [
  -    "bulk",
  -    "count",
  -    "create",
  -    "delete",
  -    "delete_by_query",
  -    "exists",
  -    "exists_source",
  -    "explain",
  -    "field_caps",
  -    "get",
  -    "get_source",
  -    "index",
  -    "mget",
  -    "msearch",
  -    "msearch_template",
  -    "mtermvectors",
  -    "reindex",
  -    "reindex_rethrottle",
  -    "search",
  -    "search_shards",
  -    "search_template",
  -    "termvectors",
  -    "update",
  -    "update_by_query",
  +    ClientMethod("bulk", True),
  +    ClientMethod("clear_scroll", False),
  +    ClientMethod("count", True),
  +    ClientMethod("create", True),
  +    ClientMethod("delete", True),
  +    ClientMethod("delete_by_query", True),
  +    ClientMethod("delete_by_query_rethrottle", False),
  +    ClientMethod("delete_script", False),
  +    ClientMethod("exists", True),
  +    ClientMethod("exists_source", True),
  +    ClientMethod("explain", True),
  +    ClientMethod("field_caps", True),
  +    ClientMethod("get", True),
  +    ClientMethod("get_script", False),
  +    ClientMethod("get_source", True),
  +    ClientMethod("index", True),
  +    ClientMethod("info", False),
  +    ClientMethod("mget", True),
  +    ClientMethod("msearch", True),
  +    ClientMethod("msearch_template", True),
  +    ClientMethod("mtermvectors", True),
  +    ClientMethod("ping", False),
  +    ClientMethod("put_script", False),
  +    ClientMethod("rank_eval", True),
  +    ClientMethod("reindex", False),
  +    ClientMethod("reindex_rethrottle", False),
  +    ClientMethod("render_search_template", False),
  +    ClientMethod("scripts_painless_context", False),
  +    ClientMethod("scripts_painless_execute", False),
  +    ClientMethod("scroll", False),
  +    ClientMethod("search", True),
  +    ClientMethod("search_shards", True),
  +    ClientMethod("search_template", True),
  +    ClientMethod("termvectors", True),
  +    ClientMethod("update", True),
  +    ClientMethod("update_by_query", True),
  +    ClientMethod("update_by_query_rethrottle", False),
   ]
   
   
  @@ -61,13 +77,14 @@
       global have_patched_client
   
       if not have_patched_client:
  -        for name in CLIENT_METHODS:
  +        for name, takes_index_argument in CLIENT_METHODS:
               try:
  -                setattr(
  -                    Elasticsearch,
  -                    name,
  -                    wrap_client_method(getattr(Elasticsearch, name)),
  -                )
  +                method = getattr(Elasticsearch, name)
  +                if takes_index_argument:
  +                    wrapped = wrap_client_index_method(method)
  +                else:
  +                    wrapped = wrap_client_method(method)
  +                setattr(Elasticsearch, name, wrapped)
               except Exception as exc:
                   logger.warning(
                       "Unable to instrument elasticsearch.Elasticsearch.%s: %r",
  @@ -80,7 +97,7 @@
   
   
   @wrapt.decorator
  -def wrap_client_method(wrapped, instance, args, kwargs):
  +def wrap_client_index_method(wrapped, instance, args, kwargs):
       def _get_index(index, *args, **kwargs):
           return index
   
  @@ -105,6 +122,19 @@
           tracked_request.stop_span()
   
   
  +@wrapt.decorator
  +def wrap_client_method(wrapped, instance, args, kwargs):
  +    camel_name = "".join(c.title() for c in wrapped.__name__.split("_"))
  +    operation = "Elasticsearch/{}".format(camel_name)
  +    tracked_request = TrackedRequest.instance()
  +    tracked_request.start_span(operation=operation, ignore_children=True)
  +
  +    try:
  +        return wrapped(*args, **kwargs)
  +    finally:
  +        tracked_request.stop_span()
  +
  +
   have_patched_transport = False
  
verification_info: {"golden_diff": "diff --git a/src/scout_apm/instruments/elasticsearch.py b/src/scout_apm/instruments/elasticsearch.py\n--- a/src/scout_apm/instruments/elasticsearch.py\n+++ b/src/scout_apm/instruments/elasticsearch.py\n@@ -2,6 +2,7 @@\n from __future__ import absolute_import, division, print_function, unicode_literals\n \n import logging\n+from collections import namedtuple\n \n import wrapt\n \n@@ -26,31 +27,46 @@\n         ensure_transport_instrumented()\n \n \n+ClientMethod = namedtuple(\"ClientMethod\", [\"name\", \"takes_index_argument\"])\n+\n CLIENT_METHODS = [\n-    \"bulk\",\n-    \"count\",\n-    \"create\",\n-    \"delete\",\n-    \"delete_by_query\",\n-    \"exists\",\n-    \"exists_source\",\n-    \"explain\",\n-    \"field_caps\",\n-    \"get\",\n-    \"get_source\",\n-    \"index\",\n-    \"mget\",\n-    \"msearch\",\n-    \"msearch_template\",\n-    \"mtermvectors\",\n-    \"reindex\",\n-    \"reindex_rethrottle\",\n-    \"search\",\n-    \"search_shards\",\n-    \"search_template\",\n-    \"termvectors\",\n-    \"update\",\n-    \"update_by_query\",\n+    ClientMethod(\"bulk\", True),\n+    ClientMethod(\"clear_scroll\", False),\n+    ClientMethod(\"count\", True),\n+    ClientMethod(\"create\", True),\n+    ClientMethod(\"delete\", True),\n+    ClientMethod(\"delete_by_query\", True),\n+    ClientMethod(\"delete_by_query_rethrottle\", False),\n+    ClientMethod(\"delete_script\", False),\n+    ClientMethod(\"exists\", True),\n+    ClientMethod(\"exists_source\", True),\n+    ClientMethod(\"explain\", True),\n+    ClientMethod(\"field_caps\", True),\n+    ClientMethod(\"get\", True),\n+    ClientMethod(\"get_script\", False),\n+    ClientMethod(\"get_source\", True),\n+    ClientMethod(\"index\", True),\n+    ClientMethod(\"info\", False),\n+    ClientMethod(\"mget\", True),\n+    ClientMethod(\"msearch\", True),\n+    ClientMethod(\"msearch_template\", True),\n+    ClientMethod(\"mtermvectors\", True),\n+    ClientMethod(\"ping\", False),\n+    ClientMethod(\"put_script\", False),\n+    ClientMethod(\"rank_eval\", True),\n+    ClientMethod(\"reindex\", False),\n+    ClientMethod(\"reindex_rethrottle\", False),\n+    ClientMethod(\"render_search_template\", False),\n+    ClientMethod(\"scripts_painless_context\", False),\n+    ClientMethod(\"scripts_painless_execute\", False),\n+    ClientMethod(\"scroll\", False),\n+    ClientMethod(\"search\", True),\n+    ClientMethod(\"search_shards\", True),\n+    ClientMethod(\"search_template\", True),\n+    ClientMethod(\"termvectors\", True),\n+    ClientMethod(\"update\", True),\n+    ClientMethod(\"update_by_query\", True),\n+    ClientMethod(\"update_by_query_rethrottle\", False),\n ]\n \n \n@@ -61,13 +77,14 @@\n     global have_patched_client\n \n     if not have_patched_client:\n-        for name in CLIENT_METHODS:\n+        for name, takes_index_argument in CLIENT_METHODS:\n             try:\n-                setattr(\n-                    Elasticsearch,\n-                    name,\n-                    wrap_client_method(getattr(Elasticsearch, name)),\n-                )\n+                method = getattr(Elasticsearch, name)\n+                if takes_index_argument:\n+                    wrapped = wrap_client_index_method(method)\n+                else:\n+                    wrapped = wrap_client_method(method)\n+                setattr(Elasticsearch, name, wrapped)\n             except Exception as exc:\n                 logger.warning(\n                     \"Unable to instrument elasticsearch.Elasticsearch.%s: %r\",\n@@ -80,7 +97,7 @@\n \n \n @wrapt.decorator\n-def wrap_client_method(wrapped, instance, args, kwargs):\n+def wrap_client_index_method(wrapped, instance, args, kwargs):\n     def _get_index(index, *args, **kwargs):\n         return index\n \n@@ -105,6 +122,19 @@\n         tracked_request.stop_span()\n \n \n+@wrapt.decorator\n+def wrap_client_method(wrapped, instance, args, kwargs):\n+    camel_name = \"\".join(c.title() for c in wrapped.__name__.split(\"_\"))\n+    operation = \"Elasticsearch/{}\".format(camel_name)\n+    tracked_request = TrackedRequest.instance()\n+    tracked_request.start_span(operation=operation, ignore_children=True)\n+\n+    try:\n+        return wrapped(*args, **kwargs)\n+    finally:\n+        tracked_request.stop_span()\n+\n+\n have_patched_transport = False\n", "issue": "Instrument non-index Elasticsearch client methods\nThere are many methods on the `elasticsearch-py` client class that we aren't instrumenting, for example `ping()`, because they don't operate on an index.\r\n\r\nWe should capture all these calls too - I'm sure many applications have such calls that would be good to show on traces.\r\n\r\nhttps://github.com/elastic/elasticsearch-py/blob/master/elasticsearch/client/__init__.py\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport logging\n\nimport wrapt\n\nfrom scout_apm.core.tracked_request import TrackedRequest\n\ntry:\n    from elasticsearch import Elasticsearch, Transport\nexcept ImportError:  # pragma: no cover\n    Elasticsearch = None\n    Transport = None\n\nlogger = logging.getLogger(__name__)\n\n\ndef ensure_installed():\n    logger.info(\"Ensuring elasticsearch instrumentation is installed.\")\n\n    if Elasticsearch is None:\n        logger.info(\"Unable to import elasticsearch.Elasticsearch\")\n    else:\n        ensure_client_instrumented()\n        ensure_transport_instrumented()\n\n\nCLIENT_METHODS = [\n    \"bulk\",\n    \"count\",\n    \"create\",\n    \"delete\",\n    \"delete_by_query\",\n    \"exists\",\n    \"exists_source\",\n    \"explain\",\n    \"field_caps\",\n    \"get\",\n    \"get_source\",\n    \"index\",\n    \"mget\",\n    \"msearch\",\n    \"msearch_template\",\n    \"mtermvectors\",\n    \"reindex\",\n    \"reindex_rethrottle\",\n    \"search\",\n    \"search_shards\",\n    \"search_template\",\n    \"termvectors\",\n    \"update\",\n    \"update_by_query\",\n]\n\n\nhave_patched_client = False\n\n\ndef ensure_client_instrumented():\n    global have_patched_client\n\n    if not have_patched_client:\n        for name in CLIENT_METHODS:\n            try:\n                setattr(\n                    Elasticsearch,\n                    name,\n                    wrap_client_method(getattr(Elasticsearch, name)),\n                )\n            except Exception as exc:\n                logger.warning(\n                    \"Unable to instrument elasticsearch.Elasticsearch.%s: %r\",\n                    name,\n                    exc,\n                    exc_info=exc,\n                )\n\n        have_patched_client = True\n\n\n@wrapt.decorator\ndef wrap_client_method(wrapped, instance, args, kwargs):\n    def _get_index(index, *args, **kwargs):\n        return index\n\n    try:\n        index = _get_index(*args, **kwargs)\n    except TypeError:\n        index = \"Unknown\"\n    else:\n        if not index:\n            index = \"Unknown\"\n        if isinstance(index, (list, tuple)):\n            index = \",\".join(index)\n    index = index.title()\n    camel_name = \"\".join(c.title() for c in wrapped.__name__.split(\"_\"))\n    operation = \"Elasticsearch/{}/{}\".format(index, camel_name)\n    tracked_request = TrackedRequest.instance()\n    tracked_request.start_span(operation=operation, ignore_children=True)\n\n    try:\n        return wrapped(*args, **kwargs)\n    finally:\n        tracked_request.stop_span()\n\n\nhave_patched_transport = False\n\n\ndef ensure_transport_instrumented():\n    global have_patched_transport\n\n    if not have_patched_transport:\n        try:\n            Transport.perform_request = wrapped_perform_request(\n                Transport.perform_request\n            )\n        except Exception as exc:\n            logger.warning(\n                \"Unable to instrument elasticsearch.Transport.perform_request: %r\",\n                exc,\n                exc_info=exc,\n            )\n\n    have_patched_transport = True\n\n\ndef _sanitize_name(name):\n    try:\n        op = name.split(\"/\")[-1]\n        op = op[1:]  # chop leading '_' from op\n        known_names = (\n            \"bench\",\n            \"bulk\",\n            \"count\",\n            \"exists\",\n            \"explain\",\n            \"field_stats\",\n            \"health\",\n            \"mget\",\n            \"mlt\",\n            \"mpercolate\",\n            \"msearch\",\n            \"mtermvectors\",\n            \"percolate\",\n            \"query\",\n            \"scroll\",\n            \"search_shards\",\n            \"source\",\n            \"suggest\",\n            \"template\",\n            \"termvectors\",\n            \"update\",\n            \"search\",\n        )\n        if op in known_names:\n            return op.title()\n        return \"Unknown\"\n    except Exception:\n        return \"Unknown\"\n\n\n@wrapt.decorator\ndef wrapped_perform_request(wrapped, instance, args, kwargs):\n    try:\n        op = _sanitize_name(args[1])\n    except IndexError:\n        op = \"Unknown\"\n\n    tracked_request = TrackedRequest.instance()\n    tracked_request.start_span(\n        operation=\"Elasticsearch/{}\".format(op), ignore_children=True\n    )\n\n    try:\n        return wrapped(*args, **kwargs)\n    finally:\n        tracked_request.stop_span()\n", "path": "src/scout_apm/instruments/elasticsearch.py"}], "after_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport logging\nfrom collections import namedtuple\n\nimport wrapt\n\nfrom scout_apm.core.tracked_request import TrackedRequest\n\ntry:\n    from elasticsearch import Elasticsearch, Transport\nexcept ImportError:  # pragma: no cover\n    Elasticsearch = None\n    Transport = None\n\nlogger = logging.getLogger(__name__)\n\n\ndef ensure_installed():\n    logger.info(\"Ensuring elasticsearch instrumentation is installed.\")\n\n    if Elasticsearch is None:\n        logger.info(\"Unable to import elasticsearch.Elasticsearch\")\n    else:\n        ensure_client_instrumented()\n        ensure_transport_instrumented()\n\n\nClientMethod = namedtuple(\"ClientMethod\", [\"name\", \"takes_index_argument\"])\n\nCLIENT_METHODS = [\n    ClientMethod(\"bulk\", True),\n    ClientMethod(\"clear_scroll\", False),\n    ClientMethod(\"count\", True),\n    ClientMethod(\"create\", True),\n    ClientMethod(\"delete\", True),\n    ClientMethod(\"delete_by_query\", True),\n    ClientMethod(\"delete_by_query_rethrottle\", False),\n    ClientMethod(\"delete_script\", False),\n    ClientMethod(\"exists\", True),\n    ClientMethod(\"exists_source\", True),\n    ClientMethod(\"explain\", True),\n    ClientMethod(\"field_caps\", True),\n    ClientMethod(\"get\", True),\n    ClientMethod(\"get_script\", False),\n    ClientMethod(\"get_source\", True),\n    ClientMethod(\"index\", True),\n    ClientMethod(\"info\", False),\n    ClientMethod(\"mget\", True),\n    ClientMethod(\"msearch\", True),\n    ClientMethod(\"msearch_template\", True),\n    ClientMethod(\"mtermvectors\", True),\n    ClientMethod(\"ping\", False),\n    ClientMethod(\"put_script\", False),\n    ClientMethod(\"rank_eval\", True),\n    ClientMethod(\"reindex\", False),\n    ClientMethod(\"reindex_rethrottle\", False),\n    ClientMethod(\"render_search_template\", False),\n    ClientMethod(\"scripts_painless_context\", False),\n    ClientMethod(\"scripts_painless_execute\", False),\n    ClientMethod(\"scroll\", False),\n    ClientMethod(\"search\", True),\n    ClientMethod(\"search_shards\", True),\n    ClientMethod(\"search_template\", True),\n    ClientMethod(\"termvectors\", True),\n    ClientMethod(\"update\", True),\n    ClientMethod(\"update_by_query\", True),\n    ClientMethod(\"update_by_query_rethrottle\", False),\n]\n\n\nhave_patched_client = False\n\n\ndef ensure_client_instrumented():\n    global have_patched_client\n\n    if not have_patched_client:\n        for name, takes_index_argument in CLIENT_METHODS:\n            try:\n                method = getattr(Elasticsearch, name)\n                if takes_index_argument:\n                    wrapped = wrap_client_index_method(method)\n                else:\n                    wrapped = wrap_client_method(method)\n                setattr(Elasticsearch, name, wrapped)\n            except Exception as exc:\n                logger.warning(\n                    \"Unable to instrument elasticsearch.Elasticsearch.%s: %r\",\n                    name,\n                    exc,\n                    exc_info=exc,\n                )\n\n        have_patched_client = True\n\n\n@wrapt.decorator\ndef wrap_client_index_method(wrapped, instance, args, kwargs):\n    def _get_index(index, *args, **kwargs):\n        return index\n\n    try:\n        index = _get_index(*args, **kwargs)\n    except TypeError:\n        index = \"Unknown\"\n    else:\n        if not index:\n            index = \"Unknown\"\n        if isinstance(index, (list, tuple)):\n            index = \",\".join(index)\n    index = index.title()\n    camel_name = \"\".join(c.title() for c in wrapped.__name__.split(\"_\"))\n    operation = \"Elasticsearch/{}/{}\".format(index, camel_name)\n    tracked_request = TrackedRequest.instance()\n    tracked_request.start_span(operation=operation, ignore_children=True)\n\n    try:\n        return wrapped(*args, **kwargs)\n    finally:\n        tracked_request.stop_span()\n\n\n@wrapt.decorator\ndef wrap_client_method(wrapped, instance, args, kwargs):\n    camel_name = \"\".join(c.title() for c in wrapped.__name__.split(\"_\"))\n    operation = \"Elasticsearch/{}\".format(camel_name)\n    tracked_request = TrackedRequest.instance()\n    tracked_request.start_span(operation=operation, ignore_children=True)\n\n    try:\n        return wrapped(*args, **kwargs)\n    finally:\n        tracked_request.stop_span()\n\n\nhave_patched_transport = False\n\n\ndef ensure_transport_instrumented():\n    global have_patched_transport\n\n    if not have_patched_transport:\n        try:\n            Transport.perform_request = wrapped_perform_request(\n                Transport.perform_request\n            )\n        except Exception as exc:\n            logger.warning(\n                \"Unable to instrument elasticsearch.Transport.perform_request: %r\",\n                exc,\n                exc_info=exc,\n            )\n\n    have_patched_transport = True\n\n\ndef _sanitize_name(name):\n    try:\n        op = name.split(\"/\")[-1]\n        op = op[1:]  # chop leading '_' from op\n        known_names = (\n            \"bench\",\n            \"bulk\",\n            \"count\",\n            \"exists\",\n            \"explain\",\n            \"field_stats\",\n            \"health\",\n            \"mget\",\n            \"mlt\",\n            \"mpercolate\",\n            \"msearch\",\n            \"mtermvectors\",\n            \"percolate\",\n            \"query\",\n            \"scroll\",\n            \"search_shards\",\n            \"source\",\n            \"suggest\",\n            \"template\",\n            \"termvectors\",\n            \"update\",\n            \"search\",\n        )\n        if op in known_names:\n            return op.title()\n        return \"Unknown\"\n    except Exception:\n        return \"Unknown\"\n\n\n@wrapt.decorator\ndef wrapped_perform_request(wrapped, instance, args, kwargs):\n    try:\n        op = _sanitize_name(args[1])\n    except IndexError:\n        op = \"Unknown\"\n\n    tracked_request = TrackedRequest.instance()\n    tracked_request.start_span(\n        operation=\"Elasticsearch/{}\".format(op), ignore_children=True\n    )\n\n    try:\n        return wrapped(*args, **kwargs)\n    finally:\n        tracked_request.stop_span()\n", "path": "src/scout_apm/instruments/elasticsearch.py"}]}