Skip to content

Handle TCP socket failure in logging.handlers.SysLogHandler.emit #122959

@gumballcmm

Description

@gumballcmm

Bug report

Bug description:

Client loggers on TCP using a socket stream can timeout. A TCP logger server could issue disconnect to TCP client due to timeout caused usually by no activity after some elapsed time. TCP servers typically have to control how many TCP listeners are active in memory to prevent TCP flooding. They do it by monitoring TCP activity timeouts and by removing open TCP sockets from memory and by sending a FIN to client to disconnect socket in remote TCP table as well.

Going back to the Python apps that use "import logging" to send events to specifically to TCP handlers, an app relying on TCP logger would not recover successfully if socket was closed by server's TCP FIN packet; thus app's syslog message/event would no longer be forwarded to TCP log collector. The bug exists because there is no verification of TCP socket stream existence in logging.handlers.SysLogHandler.emit.

If socket was torned down by Kernel it doesn't exist and socket Exception is generated and a new socket stream of type socket.SOCK_STREAM should be created and message resent to remote TCP collector on 2nd attempt. The reason why the bug exists is because the current logger handler does not support explicit try:except auto-remediation handling for socket types socket.SOCK_STREAM (TCP sockets) in logging.handlers.SysLogHandler.emit. The proposed update below addresses that gap by providing recovery support for TCP sockets that were closed by Kernel ensuring data logging continuity for Python apps without restarting the application..

Notice that there isn't a recovery block in the original code for socket type socket.SOCK_STREAM in logging.handlers.

I added the code in bold to show how the socket would be created for a Python app using TCP-based client logging as a handler. I have confirmed this bug exist with our TCP syslog infrastructure and confirmed corrected by the following code in bold. You could duplicate yourself by adding a TCP handler to any IP using import logging and then wait have the TCP server disconnect the socket sending FIN to client and the TCP client will fail to create a new socket on new event through logger. An error should appear when trying to self.socket.send(msg) when a TCP socket doesn't exist and there is no try/except to recover from that in original code.

Please approve this bug and I'll issue a pull request with the bolded code below to fix it.

From logging.handlers.SysLogHandler.emit:

if not self.socket:
    self.createSocket()

if self.unixsocket:
    try:
        self.socket.send(msg)
    except OSError:
        self.socket.close()
        self._connect_unixsocket(self.address)
        self.socket.send(msg)
+# UDP socket type
elif self.socktype == socket.SOCK_DGRAM:
    self.socket.sendto(msg, self.address)
+# TCP socket type
+# This block doesn't exist in original handlers.py code for TCP
+elif self.socktype == socket.SOCK_STREAM:
+    try:
+        self.socket.sendall(msg)
+    # create new TCP socket stream, if TCP socket broken or disconnected
+    except OSError:
+        self.socket = socket.socket(socket.AF_INET, self.socktype)
+        self.socket.connect(self.address)
+        # retry sending msg after making new TCP AF_INET socket
+        self.socket.sendall(msg)
else:
    self.socket.sendall(msg)

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.12only security fixes3.13bugs and security fixes3.14bugs and security fixesstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Status

    No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions