Skip to content

Commit 73e6f80

Browse files
committed
gh-126400: Add TCP socket timeout to SysLogHandler
1 parent 33ce8dc commit 73e6f80

File tree

3 files changed

+25
-2
lines changed

3 files changed

+25
-2
lines changed

Doc/library/logging.handlers.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,7 +613,7 @@ The :class:`SysLogHandler` class, located in the :mod:`logging.handlers` module,
613613
supports sending logging messages to a remote or local Unix syslog.
614614

615615

616-
.. class:: SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM)
616+
.. class:: SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM, timeout=None)
617617

618618
Returns a new instance of the :class:`SysLogHandler` class intended to
619619
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.
626626
*socktype* argument, which defaults to :const:`socket.SOCK_DGRAM` and thus
627627
opens a UDP socket. To open a TCP socket (for use with the newer syslog
628628
daemons such as rsyslog), specify a value of :const:`socket.SOCK_STREAM`.
629+
If *timeout* is specified, it sets a timeout (in seconds) for the socket operations.
630+
This can help prevent the program from hanging indefinitely if the syslog server is
631+
unreachable. By default, *timeout* is `None`, meaning no timeout is applied.
632+
633+
629634

630635
Note that if your server is not listening on UDP port 514,
631636
: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.
645650
.. versionchanged:: 3.2
646651
*socktype* was added.
647652

653+
.. versionchanged:: 3.x
654+
*timeout* was added.
648655

649656
.. method:: close()
650657

Lib/logging/handlers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ class SysLogHandler(logging.Handler):
855855
}
856856

857857
def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
858-
facility=LOG_USER, socktype=None):
858+
facility=LOG_USER, socktype=None, timeout=None):
859859
"""
860860
Initialize a handler.
861861
@@ -872,6 +872,7 @@ def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
872872
self.address = address
873873
self.facility = facility
874874
self.socktype = socktype
875+
self.timeout = timeout
875876
self.socket = None
876877
self.createSocket()
877878

@@ -933,6 +934,8 @@ def createSocket(self):
933934
err = sock = None
934935
try:
935936
sock = socket.socket(af, socktype, proto)
937+
if self.timeout:
938+
sock.settimeout(self.timeout)
936939
if socktype == socket.SOCK_STREAM:
937940
sock.connect(sa)
938941
break

Lib/test/test_logging.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import logging.handlers
2323
import logging.config
2424

25+
2526
import codecs
2627
import configparser
2728
import copy
@@ -2098,6 +2099,18 @@ def test_udp_reconnection(self):
20982099
self.handled.wait(support.LONG_TIMEOUT)
20992100
self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
21002101

2102+
@patch('socket.socket')
2103+
def test_tcp_timeout(self, mock_socket):
2104+
instance_mock_sock = mock_socket.return_value
2105+
instance_mock_sock.connect.side_effect = socket.timeout
2106+
2107+
with self.assertRaises(socket.timeout):
2108+
logging.handlers.SysLogHandler(address=('localhost', 514),
2109+
socktype=socket.SOCK_STREAM,
2110+
timeout=1)
2111+
2112+
instance_mock_sock.close.assert_called()
2113+
21012114
@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
21022115
class UnixSysLogHandlerTest(SysLogHandlerTest):
21032116

0 commit comments

Comments
 (0)