]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-126400: Add TCP socket timeout to SysLogHandler to prevent blocking (GH-126716)
authorHod <104125115+hodamarr@users.noreply.github.com>
Wed, 29 Jan 2025 19:37:43 +0000 (21:37 +0200)
committerGitHub <noreply@github.com>
Wed, 29 Jan 2025 19:37:43 +0000 (19:37 +0000)
Co-authored-by: Vinay Sajip <vinay_sajip@yahoo.co.uk>
Doc/library/logging.handlers.rst
Lib/logging/handlers.py
Lib/test/test_logging.py
Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst [new file with mode: 0644]

index 5a081f9e7add996c094b7a1528097d61ca2bdc56..ffb54591b3563b4f991538a323bf41129cef79bb 100644 (file)
@@ -613,7 +613,7 @@ The :class:`SysLogHandler` class, located in the :mod:`logging.handlers` module,
 supports sending logging messages to a remote or local Unix syslog.
 
 
-.. class:: SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM)
+.. class:: SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM, timeout=None)
 
    Returns a new instance of the :class:`SysLogHandler` class intended to
    communicate with a remote Unix machine whose address is given by *address* in
@@ -626,6 +626,11 @@ supports sending logging messages to a remote or local Unix syslog.
    *socktype* argument, which defaults to :const:`socket.SOCK_DGRAM` and thus
    opens a UDP socket. To open a TCP socket (for use with the newer syslog
    daemons such as rsyslog), specify a value of :const:`socket.SOCK_STREAM`.
+   If *timeout* is specified, it sets a timeout (in seconds) for the socket operations.
+   This can help prevent the program from hanging indefinitely if the syslog server is
+   unreachable. By default, *timeout* is ``None``, meaning no timeout is applied.
+
+
 
    Note that if your server is not listening on UDP port 514,
    :class:`SysLogHandler` may appear not to work. In that case, check what
@@ -645,6 +650,8 @@ supports sending logging messages to a remote or local Unix syslog.
    .. versionchanged:: 3.2
       *socktype* was added.
 
+   .. versionchanged:: 3.14
+      *timeout* was added.
 
    .. method:: close()
 
index 1cba64fd554100f3be3de60a0ae41ffeb59c28b6..017c9ab409b7bcb421945b1909c7045bede86e59 100644 (file)
@@ -855,7 +855,7 @@ class SysLogHandler(logging.Handler):
     }
 
     def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
-                 facility=LOG_USER, socktype=None):
+                 facility=LOG_USER, socktype=None, timeout=None):
         """
         Initialize a handler.
 
@@ -872,6 +872,7 @@ class SysLogHandler(logging.Handler):
         self.address = address
         self.facility = facility
         self.socktype = socktype
+        self.timeout = timeout
         self.socket = None
         self.createSocket()
 
@@ -933,6 +934,8 @@ class SysLogHandler(logging.Handler):
                 err = sock = None
                 try:
                     sock = socket.socket(af, socktype, proto)
+                    if self.timeout:
+                        sock.settimeout(self.timeout)
                     if socktype == socket.SOCK_STREAM:
                         sock.connect(sa)
                     break
index 2e5f6475ae3b1ef98d71f9b8996233b791058688..e34fe45fd68e5272a3148547aa1569abe6601222 100644 (file)
@@ -22,6 +22,7 @@ import logging
 import logging.handlers
 import logging.config
 
+
 import codecs
 import configparser
 import copy
@@ -2095,6 +2096,18 @@ class SysLogHandlerTest(BaseTest):
         self.handled.wait(support.LONG_TIMEOUT)
         self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
 
+    @patch('socket.socket')
+    def test_tcp_timeout(self, mock_socket):
+        instance_mock_sock = mock_socket.return_value
+        instance_mock_sock.connect.side_effect = socket.timeout
+
+        with self.assertRaises(socket.timeout):
+            logging.handlers.SysLogHandler(address=('localhost', 514),
+                                           socktype=socket.SOCK_STREAM,
+                                           timeout=1)
+
+        instance_mock_sock.close.assert_called()
+
 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
 class UnixSysLogHandlerTest(SysLogHandlerTest):
 
diff --git a/Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst b/Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst
new file mode 100644 (file)
index 0000000..1532faf
--- /dev/null
@@ -0,0 +1,2 @@
+Add a socket *timeout* keyword argument to
+:class:`logging.handlers.SysLogHandler`.