Skip to content

Commit 4434265

Browse files
authored
Merge pull request #797 from seapagan/feature/logging-query-params
feat: include query parameters in request logs
2 parents aa93e38 + 48e7c98 commit 4434265

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

app/middleware/logging_middleware.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@ async def dispatch(
3838

3939
# Log response (uvicorn style: client - "METHOD /path" status_code)
4040
duration = time.time() - start_time
41+
42+
# Build full path including query string if present
43+
path = request.url.path
44+
if request.url.query:
45+
path = f"{path}?{request.url.query}"
46+
4147
logger.info(
42-
f'{client_addr} - "{request.method} {request.url.path}" '
48+
f'{client_addr} - "{request.method} {path}" '
4349
f"{response.status_code} ({duration:.3f}s)"
4450
)
4551

tests/unit/test_logging.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,72 @@ async def test_middleware_logs_when_requests_enabled(
437437
assert "GET /api/users" in log_message
438438
assert "200" in log_message
439439
assert result == mock_response
440+
441+
async def test_middleware_logs_query_parameters(
442+
self, mocker: MockerFixture
443+
) -> None:
444+
"""Test middleware includes query parameters in logs."""
445+
mock_log_config = mocker.patch(
446+
"app.middleware.logging_middleware.log_config"
447+
)
448+
mock_log_config.is_enabled.return_value = True
449+
450+
mock_logger = mocker.patch("app.middleware.logging_middleware.logger")
451+
452+
middleware = LoggingMiddleware(app=Mock())
453+
454+
# Create mock request with query parameters
455+
mock_request = Mock(spec=Request)
456+
mock_request.client = Mock(host="127.0.0.1")
457+
mock_request.method = "GET"
458+
mock_request.url.path = "/api/users"
459+
mock_request.url.query = "page=2&limit=10"
460+
461+
mock_response = Mock(spec=Response)
462+
mock_response.status_code = 200
463+
mock_call_next = AsyncMock(return_value=mock_response)
464+
465+
result = await middleware.dispatch(mock_request, mock_call_next)
466+
467+
# Verify logging occurred with query parameters
468+
mock_logger.info.assert_called_once()
469+
log_message = mock_logger.info.call_args[0][0]
470+
assert "127.0.0.1" in log_message
471+
assert "GET /api/users?page=2&limit=10" in log_message
472+
assert "200" in log_message
473+
assert result == mock_response
474+
475+
async def test_middleware_logs_without_query_parameters(
476+
self, mocker: MockerFixture
477+
) -> None:
478+
"""Test middleware logs cleanly when no query parameters present."""
479+
mock_log_config = mocker.patch(
480+
"app.middleware.logging_middleware.log_config"
481+
)
482+
mock_log_config.is_enabled.return_value = True
483+
484+
mock_logger = mocker.patch("app.middleware.logging_middleware.logger")
485+
486+
middleware = LoggingMiddleware(app=Mock())
487+
488+
# Create mock request without query parameters
489+
mock_request = Mock(spec=Request)
490+
mock_request.client = Mock(host="127.0.0.1")
491+
mock_request.method = "POST"
492+
mock_request.url.path = "/api/users"
493+
mock_request.url.query = "" # Empty query string
494+
495+
mock_response = Mock(spec=Response)
496+
mock_response.status_code = 201
497+
mock_call_next = AsyncMock(return_value=mock_response)
498+
499+
result = await middleware.dispatch(mock_request, mock_call_next)
500+
501+
# Verify logging occurred without trailing ?
502+
mock_logger.info.assert_called_once()
503+
log_message = mock_logger.info.call_args[0][0]
504+
assert "127.0.0.1" in log_message
505+
assert "POST /api/users" in log_message
506+
assert "POST /api/users?" not in log_message # No trailing ?
507+
assert "201" in log_message
508+
assert result == mock_response

0 commit comments

Comments
 (0)