https://github.com/python/cpython/commit/fdcedfd3cf1a645634dee354ee349d966c56348f
commit: fdcedfd3cf1a645634dee354ee349d966c56348f
branch: main
author: Hod <[email protected]>
committer: vsajip <[email protected]>
date: 2025-01-29T19:37:43Z
summary:

gh-126400: Add TCP socket timeout to SysLogHandler to prevent blocking 
(GH-126716)

Co-authored-by: Vinay Sajip <[email protected]>

files:
A Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst
M Doc/library/logging.handlers.rst
M Lib/logging/handlers.py
M Lib/test/test_logging.py

diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst
index 5a081f9e7add99..ffb54591b3563b 100644
--- a/Doc/library/logging.handlers.rst
+++ b/Doc/library/logging.handlers.rst
@@ -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()
 
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
index 1cba64fd554100..017c9ab409b7bc 100644
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -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 @@ def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
         self.address = address
         self.facility = facility
         self.socktype = socktype
+        self.timeout = timeout
         self.socket = None
         self.createSocket()
 
@@ -933,6 +934,8 @@ def createSocket(self):
                 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
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
index 2e5f6475ae3b1e..e34fe45fd68e52 100644
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -22,6 +22,7 @@
 import logging.handlers
 import logging.config
 
+
 import codecs
 import configparser
 import copy
@@ -2095,6 +2096,18 @@ def test_udp_reconnection(self):
         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
index 00000000000000..1532faf4b7d6f5
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-01-29-13-37-18.gh-issue-126400.DaBaR3.rst
@@ -0,0 +1,2 @@
+Add a socket *timeout* keyword argument to
+:class:`logging.handlers.SysLogHandler`.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to