Skip to content

Commit 7e33544

Browse files
authored
Fix AIOHttpConnection logging to include the URL query
1 parent f72a1d7 commit 7e33544

File tree

2 files changed

+96
-6
lines changed

2 files changed

+96
-6
lines changed

elasticsearch/_async/http_aiohttp.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,8 +242,10 @@ async def perform_request(
242242
url_path = self.url_prefix + url
243243
if params:
244244
query_string = urlencode(params)
245+
url_target = "%s?%s" % (url_path, query_string)
245246
else:
246247
query_string = ""
248+
url_target = url_path
247249

248250
# There is a bug in aiohttp that disables the re-use
249251
# of the connection in the pool when method=HEAD.
@@ -324,7 +326,7 @@ async def perform_request(
324326
self.log_request_fail(
325327
method,
326328
str(url),
327-
url_path,
329+
url_target,
328330
orig_body,
329331
self.loop.time() - start,
330332
exception=e,
@@ -346,7 +348,7 @@ async def perform_request(
346348
self.log_request_fail(
347349
method,
348350
str(url),
349-
url_path,
351+
url_target,
350352
orig_body,
351353
duration,
352354
status_code=response.status,
@@ -355,7 +357,7 @@ async def perform_request(
355357
self._raise_error(response.status, raw_data)
356358

357359
self.log_request_success(
358-
method, str(url), url_path, orig_body, response.status, raw_data, duration
360+
method, str(url), url_target, orig_body, response.status, raw_data, duration
359361
)
360362

361363
return response.status, response_headers, raw_data

test_elasticsearch/test_async/test_connection.py

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import gzip
2020
import io
2121
import json
22+
import re
2223
import ssl
2324
import warnings
2425
from platform import python_version
@@ -30,7 +31,7 @@
3031

3132
from elasticsearch import AIOHttpConnection, AsyncElasticsearch, __versionstr__
3233
from elasticsearch.compat import reraise_exceptions
33-
from elasticsearch.exceptions import ConnectionError
34+
from elasticsearch.exceptions import ConnectionError, NotFoundError
3435

3536
pytestmark = pytest.mark.asyncio
3637

@@ -41,7 +42,9 @@ def gzip_decompress(data):
4142

4243

4344
class TestAIOHttpConnection:
44-
async def _get_mock_connection(self, connection_params={}, response_body=b"{}"):
45+
async def _get_mock_connection(
46+
self, connection_params={}, status_code=200, response_body=b"{}"
47+
):
4548
con = AIOHttpConnection(**connection_params)
4649
await con._create_aiohttp_session()
4750

@@ -61,7 +64,7 @@ async def read(self):
6164

6265
dummy_response = DummyResponse()
6366
dummy_response.headers = CIMultiDict()
64-
dummy_response.status = 200
67+
dummy_response.status = status_code
6568
_dummy_request.call_args = (args, kwargs)
6669
return dummy_response
6770

@@ -313,6 +316,91 @@ async def test_uncompressed_body_logged(self, logger):
313316
assert '> {"example": "body"}' == req[0][0] % req[0][1:]
314317
assert "< {}" == resp[0][0] % resp[0][1:]
315318

319+
@patch("elasticsearch.connection.base.logger")
320+
async def test_full_url_logged(self, logger):
321+
conn = await self._get_mock_connection()
322+
await conn.perform_request(
323+
"GET", "/", params={"key": "val"}, body=b'{"example": "body"}'
324+
)
325+
326+
assert logger.info.call_count == 1
327+
assert (
328+
logger.info.call_args_list[0][0][0] % logger.info.call_args_list[0][0][1:]
329+
== "GET http://localhost:9200/?key=val [status:200 request:0.000s]"
330+
)
331+
332+
conn = await self._get_mock_connection(status_code=404)
333+
with pytest.raises(NotFoundError):
334+
await conn.perform_request(
335+
"GET", "/", params={"key": "val"}, body=b'{"example": "body"}'
336+
)
337+
338+
assert logger.warning.call_count == 1
339+
assert (
340+
logger.warning.call_args_list[0][0][0]
341+
% logger.warning.call_args_list[0][0][1:]
342+
== "GET http://localhost:9200/?key=val [status:404 request:0.000s]"
343+
)
344+
345+
@patch("elasticsearch.connection.base.tracer")
346+
@patch("elasticsearch.connection.base.logger")
347+
async def test_failed_request_logs_and_traces(self, logger, tracer):
348+
conn = await self._get_mock_connection(
349+
response_body=b'{"answer": 42}', status_code=404
350+
)
351+
with pytest.raises(NotFoundError):
352+
await conn.perform_request("GET", "/", params={"param": 42}, body=b"{}")
353+
354+
# trace request
355+
assert 1 == tracer.info.call_count
356+
# trace response
357+
assert 1 == tracer.debug.call_count
358+
# log url and duration
359+
assert 1 == logger.warning.call_count
360+
assert re.match(
361+
r"^GET http://localhost:9200/\?param=42 \[status:404 request:0.[0-9]{3}s\]",
362+
logger.warning.call_args[0][0] % logger.warning.call_args[0][1:],
363+
)
364+
365+
@patch("elasticsearch.connection.base.tracer")
366+
@patch("elasticsearch.connection.base.logger")
367+
async def test_success_logs_and_traces(self, logger, tracer):
368+
conn = await self._get_mock_connection(
369+
response_body=b"""{"answer": "that's it!"}"""
370+
)
371+
await conn.perform_request(
372+
"GET",
373+
"/",
374+
{"param": 42},
375+
"""{"question": "what's that?"}""".encode("utf-8"),
376+
)
377+
378+
# trace request
379+
assert 1 == tracer.info.call_count
380+
assert (
381+
"""curl -H 'Content-Type: application/json' -XGET 'http://localhost:9200/?pretty&param=42' -d '{\n "question": "what\\u0027s that?"\n}'"""
382+
== tracer.info.call_args[0][0] % tracer.info.call_args[0][1:]
383+
)
384+
# trace response
385+
assert 1 == tracer.debug.call_count
386+
assert re.match(
387+
r'#\[200\] \(0.[0-9]{3}s\)\n#{\n# "answer": "that\\u0027s it!"\n#}',
388+
tracer.debug.call_args[0][0] % tracer.debug.call_args[0][1:],
389+
)
390+
391+
# log url and duration
392+
assert 1 == logger.info.call_count
393+
assert re.match(
394+
r"GET http://localhost:9200/\?param=42 \[status:200 request:0.[0-9]{3}s\]",
395+
logger.info.call_args[0][0] % logger.info.call_args[0][1:],
396+
)
397+
398+
# log request body and response
399+
assert 2 == logger.debug.call_count
400+
req, resp = logger.debug.call_args_list
401+
assert '> {"question": "what\'s that?"}' == req[0][0] % req[0][1:]
402+
assert '< {"answer": "that\'s it!"}' == resp[0][0] % resp[0][1:]
403+
316404
async def test_surrogatepass_into_bytes(self):
317405
buf = b"\xe4\xbd\xa0\xe5\xa5\xbd\xed\xa9\xaa"
318406
con = await self._get_mock_connection(response_body=buf)

0 commit comments

Comments
 (0)