149 lines
5.3 KiB
Diff
149 lines
5.3 KiB
Diff
From a28b8e4a9adeccdd0f68df07b29d5eba8c3d313d Mon Sep 17 00:00:00 2001
|
|
From: Kirill Pinchuk <192182+cybergrind@users.noreply.github.com>
|
|
Date: Thu, 5 Aug 2021 16:58:16 +0300
|
|
Subject: [PATCH] bpo-44291: Fix reconnection in logging.handlers.SysLogHandler
|
|
(GH-26490)
|
|
|
|
Right now SocketHandler and DatagramHandler implement such behavior:
|
|
|
|
1 - On close set self.socket = None
|
|
2 - When trying to send - make socket when it is None
|
|
|
|
SysLogHandler doesn't implement this behavior and when you close
|
|
the socket for some reason (eg. restart of uWSGI server on code change)
|
|
it leaves it in the closed state, then raises an error when you try to
|
|
send any message because it is closed.
|
|
|
|
--- Logging error ---
|
|
Traceback (most recent call last):
|
|
File "/usr/lib/python3.9/logging/handlers.py", line 959, in emit
|
|
self.socket.sendto(msg, self.address)
|
|
OSError: [Errno 9] Bad file descriptor
|
|
|
|
This patch adds reconnection logic for UDP/TCP sockets as well as UNIX
|
|
sockets.
|
|
|
|
Signed-off-by: Enzo Candotti <enzo.candotti@windriver.com>
|
|
---
|
|
Lib/logging/handlers.py | 63 ++++++++++++++++++++++++----------------
|
|
Lib/test/test_logging.py | 8 +++++
|
|
2 files changed, 46 insertions(+), 25 deletions(-)
|
|
|
|
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
|
|
index 867ef4ebc7..ff8f9e534f 100644
|
|
--- a/Lib/logging/handlers.py
|
|
+++ b/Lib/logging/handlers.py
|
|
@@ -829,6 +829,36 @@ class SysLogHandler(logging.Handler):
|
|
self.address = address
|
|
self.facility = facility
|
|
self.socktype = socktype
|
|
+ self.socket = None
|
|
+ self.createSocket()
|
|
+
|
|
+ def _connect_unixsocket(self, address):
|
|
+ use_socktype = self.socktype
|
|
+ if use_socktype is None:
|
|
+ use_socktype = socket.SOCK_DGRAM
|
|
+ self.socket = socket.socket(socket.AF_UNIX, use_socktype)
|
|
+ try:
|
|
+ self.socket.connect(address)
|
|
+ # it worked, so set self.socktype to the used type
|
|
+ self.socktype = use_socktype
|
|
+ except OSError:
|
|
+ self.socket.close()
|
|
+ if self.socktype is not None:
|
|
+ # user didn't specify falling back, so fail
|
|
+ raise
|
|
+ use_socktype = socket.SOCK_STREAM
|
|
+ self.socket = socket.socket(socket.AF_UNIX, use_socktype)
|
|
+ try:
|
|
+ self.socket.connect(address)
|
|
+ # it worked, so set self.socktype to the used type
|
|
+ self.socktype = use_socktype
|
|
+ except OSError:
|
|
+ self.socket.close()
|
|
+ raise
|
|
+
|
|
+ def createSocket(self):
|
|
+ address = self.address
|
|
+ socktype = self.socktype
|
|
|
|
if isinstance(address, str):
|
|
self.unixsocket = True
|
|
@@ -865,30 +895,6 @@ class SysLogHandler(logging.Handler):
|
|
self.socket = sock
|
|
self.socktype = socktype
|
|
|
|
- def _connect_unixsocket(self, address):
|
|
- use_socktype = self.socktype
|
|
- if use_socktype is None:
|
|
- use_socktype = socket.SOCK_DGRAM
|
|
- self.socket = socket.socket(socket.AF_UNIX, use_socktype)
|
|
- try:
|
|
- self.socket.connect(address)
|
|
- # it worked, so set self.socktype to the used type
|
|
- self.socktype = use_socktype
|
|
- except OSError:
|
|
- self.socket.close()
|
|
- if self.socktype is not None:
|
|
- # user didn't specify falling back, so fail
|
|
- raise
|
|
- use_socktype = socket.SOCK_STREAM
|
|
- self.socket = socket.socket(socket.AF_UNIX, use_socktype)
|
|
- try:
|
|
- self.socket.connect(address)
|
|
- # it worked, so set self.socktype to the used type
|
|
- self.socktype = use_socktype
|
|
- except OSError:
|
|
- self.socket.close()
|
|
- raise
|
|
-
|
|
def encodePriority(self, facility, priority):
|
|
"""
|
|
Encode the facility and priority. You can pass in strings or
|
|
@@ -908,7 +914,10 @@ class SysLogHandler(logging.Handler):
|
|
"""
|
|
self.acquire()
|
|
try:
|
|
- self.socket.close()
|
|
+ sock = self.socket
|
|
+ if sock:
|
|
+ self.socket = None
|
|
+ sock.close()
|
|
logging.Handler.close(self)
|
|
finally:
|
|
self.release()
|
|
@@ -948,6 +957,10 @@ class SysLogHandler(logging.Handler):
|
|
# Message is a string. Convert to bytes as required by RFC 5424
|
|
msg = msg.encode('utf-8')
|
|
msg = prio + msg
|
|
+
|
|
+ if not self.socket:
|
|
+ self.createSocket()
|
|
+
|
|
if self.unixsocket:
|
|
try:
|
|
self.socket.send(msg)
|
|
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
|
|
index a6cd291c9a..e795533775 100644
|
|
--- a/Lib/test/test_logging.py
|
|
+++ b/Lib/test/test_logging.py
|
|
@@ -1931,6 +1931,14 @@ class SysLogHandlerTest(BaseTest):
|
|
self.handled.wait()
|
|
self.assertEqual(self.log_output, b'<11>h\xc3\xa4m-sp\xc3\xa4m')
|
|
|
|
+ def test_udp_reconnection(self):
|
|
+ logger = logging.getLogger("slh")
|
|
+ self.sl_hdlr.close()
|
|
+ self.handled.clear()
|
|
+ logger.error("sp\xe4m")
|
|
+ self.handled.wait(0.1)
|
|
+ self.assertEqual(self.log_output, b'<11>sp\xc3\xa4m\x00')
|
|
+
|
|
@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
|
|
class UnixSysLogHandlerTest(SysLogHandlerTest):
|
|
|
|
--
|
|
2.25.1
|
|
|