Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion app/middleware/logging_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ async def dispatch(

# Log response (uvicorn style: client - "METHOD /path" status_code)
duration = time.time() - start_time

# Build full path including query string if present
path = str(request.url.path)
if request.url.query:
path = f"{path}?{request.url.query}"

logger.info(
f'{client_addr} - "{request.method} {request.url.path}" '
f'{client_addr} - "{request.method} {path}" '
f"{response.status_code} ({duration:.3f}s)"
)

Expand Down
69 changes: 69 additions & 0 deletions tests/unit/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,3 +437,72 @@ async def test_middleware_logs_when_requests_enabled(
assert "GET /api/users" in log_message
assert "200" in log_message
assert result == mock_response

async def test_middleware_logs_query_parameters(
self, mocker: MockerFixture
) -> None:
"""Test middleware includes query parameters in logs."""
mock_log_config = mocker.patch(
"app.middleware.logging_middleware.log_config"
)
mock_log_config.is_enabled.return_value = True

mock_logger = mocker.patch("app.middleware.logging_middleware.logger")

middleware = LoggingMiddleware(app=Mock())

# Create mock request with query parameters
mock_request = Mock(spec=Request)
mock_request.client = Mock(host="127.0.0.1")
mock_request.method = "GET"
mock_request.url.path = "/api/users"
mock_request.url.query = "page=2&limit=10"

mock_response = Mock(spec=Response)
mock_response.status_code = 200
mock_call_next = AsyncMock(return_value=mock_response)

result = await middleware.dispatch(mock_request, mock_call_next)

# Verify logging occurred with query parameters
mock_logger.info.assert_called_once()
log_message = mock_logger.info.call_args[0][0]
assert "127.0.0.1" in log_message
assert "GET /api/users?page=2&limit=10" in log_message
assert "200" in log_message
assert result == mock_response

async def test_middleware_logs_without_query_parameters(
self, mocker: MockerFixture
) -> None:
"""Test middleware logs cleanly when no query parameters present."""
mock_log_config = mocker.patch(
"app.middleware.logging_middleware.log_config"
)
mock_log_config.is_enabled.return_value = True

mock_logger = mocker.patch("app.middleware.logging_middleware.logger")

middleware = LoggingMiddleware(app=Mock())

# Create mock request without query parameters
mock_request = Mock(spec=Request)
mock_request.client = Mock(host="127.0.0.1")
mock_request.method = "POST"
mock_request.url.path = "/api/users"
mock_request.url.query = "" # Empty query string

mock_response = Mock(spec=Response)
mock_response.status_code = 201
mock_call_next = AsyncMock(return_value=mock_response)

result = await middleware.dispatch(mock_request, mock_call_next)

# Verify logging occurred without trailing ?
mock_logger.info.assert_called_once()
log_message = mock_logger.info.call_args[0][0]
assert "127.0.0.1" in log_message
assert "POST /api/users" in log_message
assert "POST /api/users?" not in log_message # No trailing ?
assert "201" in log_message
assert result == mock_response