Repository: encode/uvicorn

Issue:
Access logs for mounted apps doesnt not contain the root path
This change broke our monitoring a bit. We use FastAPI and mount a ASGI-app to `/graphql` route.
Since access logs are not really useful for GraphQL we filter them out in our monitoring to avoid a lot of noise.
Since this change access logs are logging `/` instead of `/graphql` which breaks this and makes our logs very noisy again.

_Originally posted by @levrik in https://github.com/encode/uvicorn/issues/1294#issuecomment-1046761532_


Discussion:
thanks for that @levrik

we should maybe use raw_path instead of path in 

```
def get_path_with_query_string(scope: WWWScope) -> str:
    path_with_query_string = urllib.parse.quote(scope["path"])
```


Thanks a lot for transitioning this into an issue. I'll give your suggestion a try and open a PR.

Resolving patch:
diff --git a/uvicorn/protocols/websockets/websockets_impl.py b/uvicorn/protocols/websockets/websockets_impl.py
--- a/uvicorn/protocols/websockets/websockets_impl.py
+++ b/uvicorn/protocols/websockets/websockets_impl.py
@@ -15,7 +15,12 @@
 
 from uvicorn.config import Config
 from uvicorn.logging import TRACE_LOG_LEVEL
-from uvicorn.protocols.utils import get_local_addr, get_remote_addr, is_ssl
+from uvicorn.protocols.utils import (
+    get_local_addr,
+    get_path_with_query_string,
+    get_remote_addr,
+    is_ssl,
+)
 from uvicorn.server import ServerState
 
 if sys.version_info < (3, 8):
@@ -254,7 +259,7 @@ async def asgi_send(self, message: "ASGISendEvent") -> None:
                 self.logger.info(
                     '%s - "WebSocket %s" [accepted]',
                     self.scope["client"],
-                    self.scope["path"],
+                    get_path_with_query_string(self.scope),
                 )
                 self.initial_response = None
                 self.accepted_subprotocol = cast(
@@ -274,7 +279,7 @@ async def asgi_send(self, message: "ASGISendEvent") -> None:
                 self.logger.info(
                     '%s - "WebSocket %s" 403',
                     self.scope["client"],
-                    self.scope["path"],
+                    get_path_with_query_string(self.scope),
                 )
                 self.initial_response = (http.HTTPStatus.FORBIDDEN, [], b"")
                 self.handshake_started_event.set()
diff --git a/uvicorn/protocols/websockets/wsproto_impl.py b/uvicorn/protocols/websockets/wsproto_impl.py
--- a/uvicorn/protocols/websockets/wsproto_impl.py
+++ b/uvicorn/protocols/websockets/wsproto_impl.py
@@ -10,7 +10,12 @@
 from wsproto.utilities import RemoteProtocolError
 
 from uvicorn.logging import TRACE_LOG_LEVEL
-from uvicorn.protocols.utils import get_local_addr, get_remote_addr, is_ssl
+from uvicorn.protocols.utils import (
+    get_local_addr,
+    get_path_with_query_string,
+    get_remote_addr,
+    is_ssl,
+)
 
 
 class WSProtocol(asyncio.Protocol):
@@ -244,7 +249,7 @@ async def send(self, message):
                 self.logger.info(
                     '%s - "WebSocket %s" [accepted]',
                     self.scope["client"],
-                    self.scope["path"],
+                    get_path_with_query_string(self.scope),
                 )
                 self.handshake_complete = True
                 subprotocol = message.get("subprotocol")
@@ -266,7 +271,7 @@ async def send(self, message):
                 self.logger.info(
                     '%s - "WebSocket %s" 403',
                     self.scope["client"],
-                    self.scope["path"],
+                    get_path_with_query_string(self.scope),
                 )
                 self.handshake_complete = True
                 self.close_sent = True


Test patch: