[Python-checkins] cpython: logging: added support for Unix domain sockets to SocketHandler and

vinay.sajip python-checkins at python.org
Fri Sep 27 19:18:50 CEST 2013


http://hg.python.org/cpython/rev/ce46195b56a9
changeset:   85810:ce46195b56a9
user:        Vinay Sajip <vinay_sajip at yahoo.co.uk>
date:        Fri Sep 27 18:18:28 2013 +0100
summary:
  logging: added support for Unix domain sockets to SocketHandler and DatagramHandler.

files:
  Doc/howto/logging.rst            |    4 +-
  Doc/library/logging.handlers.rst |    6 +
  Lib/logging/handlers.py          |   20 ++-
  Lib/test/test_logging.py         |  118 ++++++++++++++++--
  Misc/NEWS                        |    3 +
  5 files changed, 130 insertions(+), 21 deletions(-)


diff --git a/Doc/howto/logging.rst b/Doc/howto/logging.rst
--- a/Doc/howto/logging.rst
+++ b/Doc/howto/logging.rst
@@ -900,10 +900,10 @@
    disk files, rotating the log file at certain timed intervals.
 
 #. :class:`~handlers.SocketHandler` instances send messages to TCP/IP
-   sockets.
+   sockets. Since 3.4, Unix domain sockets are also supported.
 
 #. :class:`~handlers.DatagramHandler` instances send messages to UDP
-   sockets.
+   sockets. Since 3.4, Unix domain sockets are also supported.
 
 #. :class:`~handlers.SMTPHandler` instances send messages to a designated
    email address.
diff --git a/Doc/library/logging.handlers.rst b/Doc/library/logging.handlers.rst
--- a/Doc/library/logging.handlers.rst
+++ b/Doc/library/logging.handlers.rst
@@ -381,6 +381,9 @@
    Returns a new instance of the :class:`SocketHandler` class intended to
    communicate with a remote machine whose address is given by *host* and *port*.
 
+   .. versionchanged:: 3.4
+      If ``port`` is specified as ``None``, a Unix domain socket is created
+      using the value in ``host`` - otherwise, a TCP socket is created.
 
    .. method:: close()
 
@@ -466,6 +469,9 @@
    Returns a new instance of the :class:`DatagramHandler` class intended to
    communicate with a remote machine whose address is given by *host* and *port*.
 
+   .. versionchanged:: 3.4
+      If ``port`` is specified as ``None``, a Unix domain socket is created
+      using the value in ``host`` - otherwise, a TCP socket is created.
 
    .. method:: emit()
 
diff --git a/Lib/logging/handlers.py b/Lib/logging/handlers.py
--- a/Lib/logging/handlers.py
+++ b/Lib/logging/handlers.py
@@ -494,6 +494,10 @@
         logging.Handler.__init__(self)
         self.host = host
         self.port = port
+        if port is None:
+            self.address = host
+        else:
+            self.address = (host, port)
         self.sock = None
         self.closeOnError = False
         self.retryTime = None
@@ -509,7 +513,13 @@
         A factory method which allows subclasses to define the precise
         type of socket they want.
         """
-        return socket.create_connection((self.host, self.port), timeout=timeout)
+        if self.port is not None:
+            result = socket.create_connection(self.address, timeout=timeout)
+        else:
+            result = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+            result.settimeout(timeout)
+            result.connect(self.address)
+        return result
 
     def createSocket(self):
         """
@@ -643,7 +653,11 @@
         The factory method of SocketHandler is here overridden to create
         a UDP socket (SOCK_DGRAM).
         """
-        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+        if self.port is None:
+            family = socket.AF_UNIX
+        else:
+            family = socket.AF_INET
+        s = socket.socket(family, socket.SOCK_DGRAM)
         return s
 
     def send(self, s):
@@ -656,7 +670,7 @@
         """
         if self.sock is None:
             self.createSocket()
-        self.sock.sendto(s, (self.host, self.port))
+        self.sock.sendto(s, self.address)
 
 class SysLogHandler(logging.Handler):
     """
diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py
--- a/Lib/test/test_logging.py
+++ b/Lib/test/test_logging.py
@@ -59,7 +59,9 @@
     import smtpd
     from urllib.parse import urlparse, parse_qs
     from socketserver import (ThreadingUDPServer, DatagramRequestHandler,
-                              ThreadingTCPServer, StreamRequestHandler)
+                              ThreadingTCPServer, StreamRequestHandler,
+                              ThreadingUnixStreamServer,
+                              ThreadingUnixDatagramServer)
 except ImportError:
     threading = None
 try:
@@ -854,6 +856,9 @@
             super(TestTCPServer, self).server_bind()
             self.port = self.socket.getsockname()[1]
 
+    class TestUnixStreamServer(TestTCPServer):
+        address_family = socket.AF_UNIX
+
     class TestUDPServer(ControlMixin, ThreadingUDPServer):
         """
         A UDP server which is controllable using :class:`ControlMixin`.
@@ -901,6 +906,9 @@
             super(TestUDPServer, self).server_close()
             self._closed = True
 
+    class TestUnixDatagramServer(TestUDPServer):
+        address_family = socket.AF_UNIX
+
 # - end of server_helper section
 
 @unittest.skipUnless(threading, 'Threading required for this test.')
@@ -1358,17 +1366,22 @@
 
     """Test for SocketHandler objects."""
 
+    server_class = TestTCPServer
+    address = ('localhost', 0)
+
     def setUp(self):
         """Set up a TCP server to receive log messages, and a SocketHandler
         pointing to that server's address and port."""
         BaseTest.setUp(self)
-        addr = ('localhost', 0)
-        self.server = server = TestTCPServer(addr, self.handle_socket,
-                                                0.01)
+        self.server = server = self.server_class(self.address,
+                                                 self.handle_socket, 0.01)
         server.start()
         server.ready.wait()
-        self.sock_hdlr = logging.handlers.SocketHandler('localhost',
-                                                        server.port)
+        hcls = logging.handlers.SocketHandler
+        if isinstance(server.server_address, tuple):
+            self.sock_hdlr = hcls('localhost', server.port)
+        else:
+            self.sock_hdlr = hcls(server.server_address, None)
         self.log_output = ''
         self.root_logger.removeHandler(self.root_logger.handlers[0])
         self.root_logger.addHandler(self.sock_hdlr)
@@ -1426,20 +1439,45 @@
 
 
 @unittest.skipUnless(threading, 'Threading required for this test.')
+class UnixSocketHandlerTest(SocketHandlerTest):
+
+    """Test for SocketHandler with unix sockets."""
+
+    server_class = TestUnixStreamServer
+
+    def setUp(self):
+        # override the definition in the base class
+        fd, self.address = tempfile.mkstemp(prefix='test_logging_',
+                                            suffix='.sock')
+        os.close(fd)
+        os.remove(self.address)     # just need a name - file can't be present
+        SocketHandlerTest.setUp(self)
+
+    def tearDown(self):
+        SocketHandlerTest.tearDown(self)
+        os.remove(self.address)
+
+ at unittest.skipUnless(threading, 'Threading required for this test.')
 class DatagramHandlerTest(BaseTest):
 
     """Test for DatagramHandler."""
 
+    server_class = TestUDPServer
+    address = ('localhost', 0)
+
     def setUp(self):
         """Set up a UDP server to receive log messages, and a DatagramHandler
         pointing to that server's address and port."""
         BaseTest.setUp(self)
-        addr = ('localhost', 0)
-        self.server = server = TestUDPServer(addr, self.handle_datagram, 0.01)
+        self.server = server = self.server_class(self.address,
+                                                 self.handle_datagram, 0.01)
         server.start()
         server.ready.wait()
-        self.sock_hdlr = logging.handlers.DatagramHandler('localhost',
-                                                          server.port)
+        hcls = logging.handlers.DatagramHandler
+        if isinstance(server.server_address, tuple):
+            self.sock_hdlr = hcls('localhost', server.port)
+        else:
+            self.sock_hdlr = hcls(server.server_address, None)
         self.log_output = ''
         self.root_logger.removeHandler(self.root_logger.handlers[0])
         self.root_logger.addHandler(self.sock_hdlr)
@@ -1474,21 +1512,45 @@
 
 
 @unittest.skipUnless(threading, 'Threading required for this test.')
+class UnixDatagramHandlerTest(DatagramHandlerTest):
+
+    """Test for DatagramHandler using Unix sockets."""
+
+    server_class = TestUnixDatagramServer
+
+    def setUp(self):
+        # override the definition in the base class
+        fd, self.address = tempfile.mkstemp(prefix='test_logging_',
+                                            suffix='.sock')
+        os.close(fd)
+        os.remove(self.address)     # just need a name - file can't be present
+        DatagramHandlerTest.setUp(self)
+
+    def tearDown(self):
+        DatagramHandlerTest.tearDown(self)
+        os.remove(self.address)
+
+ at unittest.skipUnless(threading, 'Threading required for this test.')
 class SysLogHandlerTest(BaseTest):
 
     """Test for SysLogHandler using UDP."""
 
+    server_class = TestUDPServer
+    address = ('localhost', 0)
+
     def setUp(self):
         """Set up a UDP server to receive log messages, and a SysLogHandler
         pointing to that server's address and port."""
         BaseTest.setUp(self)
-        addr = ('localhost', 0)
-        self.server = server = TestUDPServer(addr, self.handle_datagram,
-                                                0.01)
+        self.server = server = self.server_class(self.address,
+                                                 self.handle_datagram, 0.01)
         server.start()
         server.ready.wait()
-        self.sl_hdlr = logging.handlers.SysLogHandler(('localhost',
-                                                       server.port))
+        hcls = logging.handlers.SysLogHandler
+        if isinstance(server.server_address, tuple):
+            self.sl_hdlr = hcls(('localhost', server.port))
+        else:
+            self.sl_hdlr = hcls(server.server_address)
         self.log_output = ''
         self.root_logger.removeHandler(self.root_logger.handlers[0])
         self.root_logger.addHandler(self.sl_hdlr)
@@ -1526,6 +1588,29 @@
 
 
 @unittest.skipUnless(threading, 'Threading required for this test.')
+class UnixSysLogHandlerTest(SysLogHandlerTest):
+
+    """Test for SysLogHandler with Unix sockets."""
+
+    server_class = TestUnixDatagramServer
+
+    def setUp(self):
+        # override the definition in the base class
+        fd, self.address = tempfile.mkstemp(prefix='test_logging_',
+                                            suffix='.sock')
+        os.close(fd)
+        os.remove(self.address)     # just need a name - file can't be present
+        SysLogHandlerTest.setUp(self)
+
+    def tearDown(self):
+        SysLogHandlerTest.tearDown(self)
+        os.remove(self.address)
+
+#    def test_output(self):
+#        import pdb; pdb.set_trace()
+#        SysLogHandlerTest.test_output(self)
+
+ at unittest.skipUnless(threading, 'Threading required for this test.')
 class HTTPHandlerTest(BaseTest):
     """Test for HTTPHandler."""
 
@@ -4034,7 +4119,8 @@
                  SMTPHandlerTest, FileHandlerTest, RotatingFileHandlerTest,
                  LastResortTest, LogRecordTest, ExceptionTest,
                  SysLogHandlerTest, HTTPHandlerTest, NTEventLogHandlerTest,
-                 TimedRotatingFileHandlerTest
+                 TimedRotatingFileHandlerTest, UnixSocketHandlerTest,
+                 UnixDatagramHandlerTest, UnixSysLogHandlerTest
                 )
 
 if __name__ == "__main__":
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -15,6 +15,9 @@
 Library
 -------
 
+- logging: added support for Unix domain sockets to SocketHandler and
+  DatagramHandler.
+
 - Issue #18996: TestCase.assertEqual() now more cleverly shorten differing
   strings in error report.
 

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list