prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Websocket logger uses sanic.log.error_logger
  Hey there,
  
  Why do we see:
  
      sanic.error - INFO - connection open
  
  via stderr when getting new websocket connections. Shouldn't this go to stdout?
  
  Also, is it possible to add "middleware" so we can properly log websocket connections and disconnects?  Is it possible to get a callback on websocket disconnects? 
  
  Thanks!
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `sanic/server/protocols/websocket_protocol.py`
  Content:
  ```
  1 from typing import TYPE_CHECKING, Optional, Sequence, cast
  2 
  3 from websockets.connection import CLOSED, CLOSING, OPEN
  4 from websockets.server import ServerConnection
  5 from websockets.typing import Subprotocol
  6 
  7 from sanic.exceptions import ServerError
  8 from sanic.log import error_logger
  9 from sanic.server import HttpProtocol
  10 
  11 from ..websockets.impl import WebsocketImplProtocol
  12 
  13 
  14 if TYPE_CHECKING:  # no cov
  15     from websockets import http11
  16 
  17 
  18 class WebSocketProtocol(HttpProtocol):
  19     __slots__ = (
  20         "websocket",
  21         "websocket_timeout",
  22         "websocket_max_size",
  23         "websocket_ping_interval",
  24         "websocket_ping_timeout",
  25     )
  26 
  27     def __init__(
  28         self,
  29         *args,
  30         websocket_timeout: float = 10.0,
  31         websocket_max_size: Optional[int] = None,
  32         websocket_ping_interval: Optional[float] = 20.0,
  33         websocket_ping_timeout: Optional[float] = 20.0,
  34         **kwargs,
  35     ):
  36         super().__init__(*args, **kwargs)
  37         self.websocket: Optional[WebsocketImplProtocol] = None
  38         self.websocket_timeout = websocket_timeout
  39         self.websocket_max_size = websocket_max_size
  40         self.websocket_ping_interval = websocket_ping_interval
  41         self.websocket_ping_timeout = websocket_ping_timeout
  42 
  43     def connection_lost(self, exc):
  44         if self.websocket is not None:
  45             self.websocket.connection_lost(exc)
  46         super().connection_lost(exc)
  47 
  48     def data_received(self, data):
  49         if self.websocket is not None:
  50             self.websocket.data_received(data)
  51         else:
  52             # Pass it to HttpProtocol handler first
  53             # That will (hopefully) upgrade it to a websocket.
  54             super().data_received(data)
  55 
  56     def eof_received(self) -> Optional[bool]:
  57         if self.websocket is not None:
  58             return self.websocket.eof_received()
  59         else:
  60             return False
  61 
  62     def close(self, timeout: Optional[float] = None):
  63         # Called by HttpProtocol at the end of connection_task
  64         # If we've upgraded to websocket, we do our own closing
  65         if self.websocket is not None:
  66             # Note, we don't want to use websocket.close()
  67             # That is used for user's application code to send a
  68             # websocket close packet. This is different.
  69             self.websocket.end_connection(1001)
  70         else:
  71             super().close()
  72 
  73     def close_if_idle(self):
  74         # Called by Sanic Server when shutting down
  75         # If we've upgraded to websocket, shut it down
  76         if self.websocket is not None:
  77             if self.websocket.connection.state in (CLOSING, CLOSED):
  78                 return True
  79             elif self.websocket.loop is not None:
  80                 self.websocket.loop.create_task(self.websocket.close(1001))
  81             else:
  82                 self.websocket.end_connection(1001)
  83         else:
  84             return super().close_if_idle()
  85 
  86     async def websocket_handshake(
  87         self, request, subprotocols: Optional[Sequence[str]] = None
  88     ):
  89         # let the websockets package do the handshake with the client
  90         try:
  91             if subprotocols is not None:
  92                 # subprotocols can be a set or frozenset,
  93                 # but ServerConnection needs a list
  94                 subprotocols = cast(
  95                     Optional[Sequence[Subprotocol]],
  96                     list(
  97                         [
  98                             Subprotocol(subprotocol)
  99                             for subprotocol in subprotocols
  100                         ]
  101                     ),
  102                 )
  103             ws_conn = ServerConnection(
  104                 max_size=self.websocket_max_size,
  105                 subprotocols=subprotocols,
  106                 state=OPEN,
  107                 logger=error_logger,
  108             )
  109             resp: "http11.Response" = ws_conn.accept(request)
  110         except Exception:
  111             msg = (
  112                 "Failed to open a WebSocket connection.\n"
  113                 "See server log for more information.\n"
  114             )
  115             raise ServerError(msg, status_code=500)
  116         if 100 <= resp.status_code <= 299:
  117             first_line = (
  118                 f"HTTP/1.1 {resp.status_code} {resp.reason_phrase}\r\n"
  119             ).encode()
  120             rbody = bytearray(first_line)
  121             rbody += (
  122                 "".join([f"{k}: {v}\r\n" for k, v in resp.headers.items()])
  123             ).encode()
  124             rbody += b"\r\n"
  125             if resp.body is not None:
  126                 rbody += resp.body
  127                 rbody += b"\r\n\r\n"
  128             await super().send(rbody)
  129         else:
  130             raise ServerError(resp.body, resp.status_code)
  131         self.websocket = WebsocketImplProtocol(
  132             ws_conn,
  133             ping_interval=self.websocket_ping_interval,
  134             ping_timeout=self.websocket_ping_timeout,
  135             close_timeout=self.websocket_timeout,
  136         )
  137         loop = (
  138             request.transport.loop
  139             if hasattr(request, "transport")
  140             and hasattr(request.transport, "loop")
  141             else None
  142         )
  143         await self.websocket.connection_made(self, loop=loop)
  144         return self.websocket
  145
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/sanic/server/protocols/websocket_protocol.py b/sanic/server/protocols/websocket_protocol.py
  --- a/sanic/server/protocols/websocket_protocol.py
  +++ b/sanic/server/protocols/websocket_protocol.py
  @@ -5,7 +5,7 @@
   from websockets.typing import Subprotocol
   
   from sanic.exceptions import ServerError
  -from sanic.log import error_logger
  +from sanic.log import logger
   from sanic.server import HttpProtocol
   
   from ..websockets.impl import WebsocketImplProtocol
  @@ -104,7 +104,7 @@
                   max_size=self.websocket_max_size,
                   subprotocols=subprotocols,
                   state=OPEN,
  -                logger=error_logger,
  +                logger=logger,
               )
               resp: "http11.Response" = ws_conn.accept(request)
           except Exception:
  
verification_info: {"golden_diff": "diff --git a/sanic/server/protocols/websocket_protocol.py b/sanic/server/protocols/websocket_protocol.py\n--- a/sanic/server/protocols/websocket_protocol.py\n+++ b/sanic/server/protocols/websocket_protocol.py\n@@ -5,7 +5,7 @@\n from websockets.typing import Subprotocol\n \n from sanic.exceptions import ServerError\n-from sanic.log import error_logger\n+from sanic.log import logger\n from sanic.server import HttpProtocol\n \n from ..websockets.impl import WebsocketImplProtocol\n@@ -104,7 +104,7 @@\n                 max_size=self.websocket_max_size,\n                 subprotocols=subprotocols,\n                 state=OPEN,\n-                logger=error_logger,\n+                logger=logger,\n             )\n             resp: \"http11.Response\" = ws_conn.accept(request)\n         except Exception:\n", "issue": "Websocket logger uses sanic.log.error_logger\nHey there,\r\n\r\nWhy do we see:\r\n\r\n    sanic.error - INFO - connection open\r\n\r\nvia stderr when getting new websocket connections. Shouldn't this go to stdout?\r\n\r\nAlso, is it possible to add \"middleware\" so we can properly log websocket connections and disconnects?  Is it possible to get a callback on websocket disconnects? \r\n\r\nThanks!\n", "before_files": [{"content": "from typing import TYPE_CHECKING, Optional, Sequence, cast\n\nfrom websockets.connection import CLOSED, CLOSING, OPEN\nfrom websockets.server import ServerConnection\nfrom websockets.typing import Subprotocol\n\nfrom sanic.exceptions import ServerError\nfrom sanic.log import error_logger\nfrom sanic.server import HttpProtocol\n\nfrom ..websockets.impl import WebsocketImplProtocol\n\n\nif TYPE_CHECKING:  # no cov\n    from websockets import http11\n\n\nclass WebSocketProtocol(HttpProtocol):\n    __slots__ = (\n        \"websocket\",\n        \"websocket_timeout\",\n        \"websocket_max_size\",\n        \"websocket_ping_interval\",\n        \"websocket_ping_timeout\",\n    )\n\n    def __init__(\n        self,\n        *args,\n        websocket_timeout: float = 10.0,\n        websocket_max_size: Optional[int] = None,\n        websocket_ping_interval: Optional[float] = 20.0,\n        websocket_ping_timeout: Optional[float] = 20.0,\n        **kwargs,\n    ):\n        super().__init__(*args, **kwargs)\n        self.websocket: Optional[WebsocketImplProtocol] = None\n        self.websocket_timeout = websocket_timeout\n        self.websocket_max_size = websocket_max_size\n        self.websocket_ping_interval = websocket_ping_interval\n        self.websocket_ping_timeout = websocket_ping_timeout\n\n    def connection_lost(self, exc):\n        if self.websocket is not None:\n            self.websocket.connection_lost(exc)\n        super().connection_lost(exc)\n\n    def data_received(self, data):\n        if self.websocket is not None:\n            self.websocket.data_received(data)\n        else:\n            # Pass it to HttpProtocol handler first\n            # That will (hopefully) upgrade it to a websocket.\n            super().data_received(data)\n\n    def eof_received(self) -> Optional[bool]:\n        if self.websocket is not None:\n            return self.websocket.eof_received()\n        else:\n            return False\n\n    def close(self, timeout: Optional[float] = None):\n        # Called by HttpProtocol at the end of connection_task\n        # If we've upgraded to websocket, we do our own closing\n        if self.websocket is not None:\n            # Note, we don't want to use websocket.close()\n            # That is used for user's application code to send a\n            # websocket close packet. This is different.\n            self.websocket.end_connection(1001)\n        else:\n            super().close()\n\n    def close_if_idle(self):\n        # Called by Sanic Server when shutting down\n        # If we've upgraded to websocket, shut it down\n        if self.websocket is not None:\n            if self.websocket.connection.state in (CLOSING, CLOSED):\n                return True\n            elif self.websocket.loop is not None:\n                self.websocket.loop.create_task(self.websocket.close(1001))\n            else:\n                self.websocket.end_connection(1001)\n        else:\n            return super().close_if_idle()\n\n    async def websocket_handshake(\n        self, request, subprotocols: Optional[Sequence[str]] = None\n    ):\n        # let the websockets package do the handshake with the client\n        try:\n            if subprotocols is not None:\n                # subprotocols can be a set or frozenset,\n                # but ServerConnection needs a list\n                subprotocols = cast(\n                    Optional[Sequence[Subprotocol]],\n                    list(\n                        [\n                            Subprotocol(subprotocol)\n                            for subprotocol in subprotocols\n                        ]\n                    ),\n                )\n            ws_conn = ServerConnection(\n                max_size=self.websocket_max_size,\n                subprotocols=subprotocols,\n                state=OPEN,\n                logger=error_logger,\n            )\n            resp: \"http11.Response\" = ws_conn.accept(request)\n        except Exception:\n            msg = (\n                \"Failed to open a WebSocket connection.\\n\"\n                \"See server log for more information.\\n\"\n            )\n            raise ServerError(msg, status_code=500)\n        if 100 <= resp.status_code <= 299:\n            first_line = (\n                f\"HTTP/1.1 {resp.status_code} {resp.reason_phrase}\\r\\n\"\n            ).encode()\n            rbody = bytearray(first_line)\n            rbody += (\n                \"\".join([f\"{k}: {v}\\r\\n\" for k, v in resp.headers.items()])\n            ).encode()\n            rbody += b\"\\r\\n\"\n            if resp.body is not None:\n                rbody += resp.body\n                rbody += b\"\\r\\n\\r\\n\"\n            await super().send(rbody)\n        else:\n            raise ServerError(resp.body, resp.status_code)\n        self.websocket = WebsocketImplProtocol(\n            ws_conn,\n            ping_interval=self.websocket_ping_interval,\n            ping_timeout=self.websocket_ping_timeout,\n            close_timeout=self.websocket_timeout,\n        )\n        loop = (\n            request.transport.loop\n            if hasattr(request, \"transport\")\n            and hasattr(request.transport, \"loop\")\n            else None\n        )\n        await self.websocket.connection_made(self, loop=loop)\n        return self.websocket\n", "path": "sanic/server/protocols/websocket_protocol.py"}], "after_files": [{"content": "from typing import TYPE_CHECKING, Optional, Sequence, cast\n\nfrom websockets.connection import CLOSED, CLOSING, OPEN\nfrom websockets.server import ServerConnection\nfrom websockets.typing import Subprotocol\n\nfrom sanic.exceptions import ServerError\nfrom sanic.log import logger\nfrom sanic.server import HttpProtocol\n\nfrom ..websockets.impl import WebsocketImplProtocol\n\n\nif TYPE_CHECKING:  # no cov\n    from websockets import http11\n\n\nclass WebSocketProtocol(HttpProtocol):\n    __slots__ = (\n        \"websocket\",\n        \"websocket_timeout\",\n        \"websocket_max_size\",\n        \"websocket_ping_interval\",\n        \"websocket_ping_timeout\",\n    )\n\n    def __init__(\n        self,\n        *args,\n        websocket_timeout: float = 10.0,\n        websocket_max_size: Optional[int] = None,\n        websocket_ping_interval: Optional[float] = 20.0,\n        websocket_ping_timeout: Optional[float] = 20.0,\n        **kwargs,\n    ):\n        super().__init__(*args, **kwargs)\n        self.websocket: Optional[WebsocketImplProtocol] = None\n        self.websocket_timeout = websocket_timeout\n        self.websocket_max_size = websocket_max_size\n        self.websocket_ping_interval = websocket_ping_interval\n        self.websocket_ping_timeout = websocket_ping_timeout\n\n    def connection_lost(self, exc):\n        if self.websocket is not None:\n            self.websocket.connection_lost(exc)\n        super().connection_lost(exc)\n\n    def data_received(self, data):\n        if self.websocket is not None:\n            self.websocket.data_received(data)\n        else:\n            # Pass it to HttpProtocol handler first\n            # That will (hopefully) upgrade it to a websocket.\n            super().data_received(data)\n\n    def eof_received(self) -> Optional[bool]:\n        if self.websocket is not None:\n            return self.websocket.eof_received()\n        else:\n            return False\n\n    def close(self, timeout: Optional[float] = None):\n        # Called by HttpProtocol at the end of connection_task\n        # If we've upgraded to websocket, we do our own closing\n        if self.websocket is not None:\n            # Note, we don't want to use websocket.close()\n            # That is used for user's application code to send a\n            # websocket close packet. This is different.\n            self.websocket.end_connection(1001)\n        else:\n            super().close()\n\n    def close_if_idle(self):\n        # Called by Sanic Server when shutting down\n        # If we've upgraded to websocket, shut it down\n        if self.websocket is not None:\n            if self.websocket.connection.state in (CLOSING, CLOSED):\n                return True\n            elif self.websocket.loop is not None:\n                self.websocket.loop.create_task(self.websocket.close(1001))\n            else:\n                self.websocket.end_connection(1001)\n        else:\n            return super().close_if_idle()\n\n    async def websocket_handshake(\n        self, request, subprotocols: Optional[Sequence[str]] = None\n    ):\n        # let the websockets package do the handshake with the client\n        try:\n            if subprotocols is not None:\n                # subprotocols can be a set or frozenset,\n                # but ServerConnection needs a list\n                subprotocols = cast(\n                    Optional[Sequence[Subprotocol]],\n                    list(\n                        [\n                            Subprotocol(subprotocol)\n                            for subprotocol in subprotocols\n                        ]\n                    ),\n                )\n            ws_conn = ServerConnection(\n                max_size=self.websocket_max_size,\n                subprotocols=subprotocols,\n                state=OPEN,\n                logger=logger,\n            )\n            resp: \"http11.Response\" = ws_conn.accept(request)\n        except Exception:\n            msg = (\n                \"Failed to open a WebSocket connection.\\n\"\n                \"See server log for more information.\\n\"\n            )\n            raise ServerError(msg, status_code=500)\n        if 100 <= resp.status_code <= 299:\n            first_line = (\n                f\"HTTP/1.1 {resp.status_code} {resp.reason_phrase}\\r\\n\"\n            ).encode()\n            rbody = bytearray(first_line)\n            rbody += (\n                \"\".join([f\"{k}: {v}\\r\\n\" for k, v in resp.headers.items()])\n            ).encode()\n            rbody += b\"\\r\\n\"\n            if resp.body is not None:\n                rbody += resp.body\n                rbody += b\"\\r\\n\\r\\n\"\n            await super().send(rbody)\n        else:\n            raise ServerError(resp.body, resp.status_code)\n        self.websocket = WebsocketImplProtocol(\n            ws_conn,\n            ping_interval=self.websocket_ping_interval,\n            ping_timeout=self.websocket_ping_timeout,\n            close_timeout=self.websocket_timeout,\n        )\n        loop = (\n            request.transport.loop\n            if hasattr(request, \"transport\")\n            and hasattr(request.transport, \"loop\")\n            else None\n        )\n        await self.websocket.connection_made(self, loop=loop)\n        return self.websocket\n", "path": "sanic/server/protocols/websocket_protocol.py"}]}