Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 = 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