Repository: scoutapp/scout_apm_python

Issue:
Better handle newly added ElasticSearch functions
When using the agent with an older version of ElasticSearch, the following warning is logged:

```
Failed to instrument elasticsearch.Elasticsearch.search_mvt: AttributeError("type object 'Elasticsearch' has no attribute 'search_mvt'")
```

When a client method doesn't exist, the agent should either ignore it or more quietly log that information.


Discussion:

Resolving patch:
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
@@ -86,6 +86,7 @@ def ensure_client_instrumented():
     global have_patched_client
 
     if not have_patched_client:
+        instrumented_count = 0
         for name, takes_index_argument in CLIENT_METHODS:
             try:
                 method = getattr(Elasticsearch, name)
@@ -94,13 +95,19 @@ def ensure_client_instrumented():
                 else:
                     wrapped = wrap_client_method(method)
                 setattr(Elasticsearch, name, wrapped)
+                instrumented_count += 1
             except Exception as exc:
-                logger.warning(
+                logger.debug(
                     "Failed to instrument elasticsearch.Elasticsearch.%s: %r",
                     name,
                     exc,
                     exc_info=exc,
                 )
+        if instrumented_count == 0:
+            logger.warning(
+                "Failed to instrument any elasticsearch.Elasticsearch methods."
+                " Enable debug logs to view root causes."
+            )
 
         have_patched_client = True
 


Test patch:
diff --git a/tests/integration/instruments/test_elasticsearch.py b/tests/integration/instruments/test_elasticsearch.py
--- a/tests/integration/instruments/test_elasticsearch.py
+++ b/tests/integration/instruments/test_elasticsearch.py
@@ -107,12 +107,35 @@ def test_ensure_installed_fail_no_client_bulk(caplog):
     )
     logger, level, message = caplog.record_tuples[1]
     assert logger == "scout_apm.instruments.elasticsearch"
-    assert level == logging.WARNING
+    assert level == logging.DEBUG
     assert message.startswith(
         "Failed to instrument elasticsearch.Elasticsearch.bulk: AttributeError"
     )
 
 
+def test_ensure_installed_fail_no_client_methods(caplog):
+    mock_not_patched = mock.patch(
+        "scout_apm.instruments.elasticsearch.have_patched_client", new=False
+    )
+    # Change the wrap methods to a boolean to cause an exception
+    # and fail to wrap any of the client methods.
+    mock_wrap_client_index_method = mock.patch(
+        "scout_apm.instruments.elasticsearch.wrap_client_index_method", new=False
+    )
+    mock_wrap_client_method = mock.patch(
+        "scout_apm.instruments.elasticsearch.wrap_client_method", new=False
+    )
+    with mock_not_patched, mock_wrap_client_index_method, mock_wrap_client_method:
+        ensure_installed()
+
+    logger, level, message = caplog.record_tuples[-1]
+    assert logger == "scout_apm.instruments.elasticsearch"
+    assert level == logging.WARNING
+    assert message.startswith(
+        "Failed to instrument any elasticsearch.Elasticsearch methods."
+    )
+
+
 def test_ensure_installed_fail_no_transport_perform_request(caplog):
     mock_not_patched = mock.patch(
         "scout_apm.instruments.elasticsearch.have_patched_transport", new=False