Skip to content

Commit 130ff0f

Browse files
gh-59060: Support IPv6 in logging.handlers.DatagramHandler
It always created an IPv4 socket, so sending to an IPv6 address failed and the log record was silently lost. Create a socket of the family of the resolved address if the host has no IPv4 address. Also add a timeout to the waits in DatagramHandlerTest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 3479e45 commit 130ff0f

5 files changed

Lines changed: 42 additions & 3 deletions

File tree

Doc/library/logging.handlers.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -594,6 +594,9 @@ over UDP sockets.
594594
If ``port`` is specified as ``None``, a Unix domain socket is created
595595
using the value in ``host`` - otherwise, a UDP socket is created.
596596

597+
.. versionchanged:: next
598+
Added support for IPv6.
599+
597600
.. method:: emit()
598601

599602
Pickles the record's attribute dictionary and writes it to the socket in

Doc/whatsnew/3.16.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ logging
349349
before the rotation interval expires.
350350
(Contributed by Iván Márton and Serhiy Storchaka in :gh:`84649`.)
351351

352+
* :class:`~logging.handlers.DatagramHandler` now supports IPv6.
353+
Previously it always created an IPv4 socket, so sending to an IPv6 address
354+
failed and the log record was silently lost.
355+
(Contributed by Serhiy Storchaka in :gh:`59060`.)
356+
352357

353358
lzma
354359
----

Lib/logging/handlers.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,16 @@ def makeSocket(self):
758758
family = socket.AF_UNIX
759759
else:
760760
family = socket.AF_INET
761+
# The host can have no IPv4 address, for example if it is an
762+
# IPv6 address. Leave resolution errors to sendto().
763+
try:
764+
infos = socket.getaddrinfo(self.host, self.port,
765+
type=socket.SOCK_DGRAM)
766+
except OSError:
767+
pass
768+
else:
769+
if not any(info[0] == family for info in infos):
770+
family = infos[0][0]
761771
s = socket.socket(family, socket.SOCK_DGRAM)
762772
return s
763773

Lib/test/test_logging.py

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,7 +1982,7 @@ def setUp(self):
19821982
server.ready.wait()
19831983
hcls = logging.handlers.DatagramHandler
19841984
if isinstance(server.server_address, tuple):
1985-
self.sock_hdlr = hcls('localhost', server.port)
1985+
self.sock_hdlr = hcls(self.address[0], server.port)
19861986
else:
19871987
self.sock_hdlr = hcls(server.server_address, None)
19881988
self.log_output = ''
@@ -2015,12 +2015,29 @@ def test_output(self):
20152015
self.skipTest(self.server_exception)
20162016
logger = logging.getLogger("udp")
20172017
logger.error("spam")
2018-
self.handled.wait()
2018+
self.handled.wait(support.LONG_TIMEOUT)
20192019
self.handled.clear()
20202020
logger.error("eggs")
2021-
self.handled.wait()
2021+
self.handled.wait(support.LONG_TIMEOUT)
20222022
self.assertEqual(self.log_output, "spam\neggs\n")
20232023

2024+
@unittest.skipUnless(socket_helper.IPV6_ENABLED,
2025+
'IPv6 support required for this test.')
2026+
class IPv6DatagramHandlerTest(DatagramHandlerTest):
2027+
2028+
"""Test for DatagramHandler with IPv6 host."""
2029+
2030+
server_class = TestUDPServer
2031+
address = ('::1', 0)
2032+
2033+
def setUp(self):
2034+
self.server_class.address_family = socket.AF_INET6
2035+
super().setUp()
2036+
2037+
def tearDown(self):
2038+
self.server_class.address_family = socket.AF_INET
2039+
super().tearDown()
2040+
20242041
@unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
20252042
class UnixDatagramHandlerTest(DatagramHandlerTest):
20262043

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:class:`logging.handlers.DatagramHandler` now supports IPv6:
2+
it creates an IPv6 socket if the host has no IPv4 address.
3+
Previously it always created an IPv4 socket,
4+
so the log records were silently lost.

0 commit comments

Comments
 (0)