Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Doc/library/logging.handlers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -626,13 +626,17 @@

.. 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

Check warning on line 629 in Doc/library/logging.handlers.rst

View workflow job for this annotation

GitHub Actions / Docs / Docs

py:const reference target not found: LOG_USER [ref.const]
communicate with a remote Unix machine whose address is given by *address* in
the form of a ``(host, port)`` tuple. If *address* is not specified,
``('localhost', 514)`` is used. The address is used to open a socket. An
alternative to providing a ``(host, port)`` tuple is providing an address as a
string, for example '/dev/log'. In this case, a Unix domain socket is used to
send the message to the syslog. If *facility* is not specified,
send the message to the syslog.
If *address* is ``None``, the :mod:`syslog` module is used to log to the
local system logger; this works even where there is no syslog socket, such
as on recent versions of macOS.
If *facility* is not specified,
:const:`LOG_USER` is used. The type of socket opened depends on the
*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
Expand All @@ -654,7 +658,8 @@

.. note:: On macOS 12.x (Monterey), Apple has changed the behaviour of their
syslog daemon - it no longer listens on a domain socket. Therefore, you cannot
expect :class:`SysLogHandler` to work on this system.
expect :class:`SysLogHandler` to work on this system with the default
*address*. Pass ``address=None`` to use the :mod:`syslog` module instead.

See :gh:`91070` for more information.

Expand All @@ -664,6 +669,9 @@
.. versionchanged:: 3.14
*timeout* was added.

.. versionchanged:: next
*address* can now be ``None`` to use the :mod:`syslog` module.

.. method:: close()

Closes the socket to the remote host.
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,11 @@ logging
before the rotation interval expires.
(Contributed by Iván Márton and Serhiy Storchaka in :gh:`84649`.)

* :class:`~logging.handlers.SysLogHandler` now accepts ``address=None`` to log
to the local system logger via the :mod:`syslog` module, which works even
where there is no syslog socket, such as on recent versions of macOS.
(Contributed by Serhiy Storchaka in :gh:`96339`.)


lzma
----
Expand Down
23 changes: 18 additions & 5 deletions Lib/logging/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,9 @@ def __init__(self, address=('localhost', SYSLOG_UDP_PORT),

If address is specified as a string, a UNIX socket is used. To log to a
local syslogd, "SysLogHandler(address="/dev/log")" can be used.
If address is None, the syslog module is used to log to the local
system logger; this works even where there is no syslog socket, such
as on recent versions of macOS.
If facility is not specified, LOG_USER is used. If socktype is
specified as socket.SOCK_DGRAM or socket.SOCK_STREAM, that specific
socket type will be used. For Unix sockets, you can also specify a
Expand All @@ -901,7 +904,12 @@ def __init__(self, address=('localhost', SYSLOG_UDP_PORT),
self.socktype = socktype
self.timeout = timeout
self.socket = None
self.createSocket()
if address is None:
# Use the syslog module to log to the local system logger.
import syslog
self.syslog = syslog
else:
self.createSocket()

def _connect_unixsocket(self, address):
use_socktype = self.socktype
Expand Down Expand Up @@ -1023,13 +1031,18 @@ def emit(self, record):
msg = self.format(record)
if self.ident:
msg = self.ident + msg
if self.append_nul:
msg += '\000'

# We need to convert record level to lowercase, maybe this will
# change in the future.
prio = '<%d>' % self.encodePriority(self.facility,
self.mapPriority(record.levelname))
prio = self.encodePriority(self.facility,
self.mapPriority(record.levelname))
if self.address is None:
self.syslog.syslog(prio, msg)
return

if self.append_nul:
msg += '\000'
prio = '<%d>' % prio
prio = prio.encode('utf-8')
# Message is a string. Convert to bytes as required by RFC 5424
msg = msg.encode('utf-8')
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
except ImportError:
win32evtlog = win32evtlogutil = pywintypes = None

try:
import syslog
except ImportError:
syslog = None

try:
import zlib
except ImportError:
Expand Down Expand Up @@ -2155,6 +2160,23 @@ def tearDown(self):
self.server_class.address_family = socket.AF_INET
super(IPv6SysLogHandlerTest, self).tearDown()

@unittest.skipUnless(syslog, 'syslog module required')
class LocalSysLogHandlerTest(BaseTest):

"""Test for SysLogHandler using the syslog module (address=None)."""

def test_emit(self):
h = logging.handlers.SysLogHandler(address=None)
self.addCleanup(h.close)
h.setFormatter(logging.Formatter('%(message)s'))
record = self.next_message()
logrec = logging.makeLogRecord({'msg': record, 'levelname': 'WARNING',
'levelno': logging.WARNING})
with patch.object(syslog, 'syslog') as mock_syslog:
h.emit(logrec)
mock_syslog.assert_called_once_with(
syslog.LOG_USER | syslog.LOG_WARNING, record)

@support.requires_working_socket()
@threading_helper.requires_working_threading()
class HTTPHandlerTest(BaseTest):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:class:`logging.handlers.SysLogHandler` now accepts ``address=None`` to log to
the local system logger via the :mod:`syslog` module. This works even where
there is no syslog socket, such as on recent versions of macOS.
Loading