Skip to content

Commit

Permalink
Add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
anodos325 committed Dec 24, 2024
1 parent 55d285e commit 0c5711d
Showing 1 changed file with 58 additions and 5 deletions.
63 changes: 58 additions & 5 deletions src/middlewared/middlewared/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,31 @@ def __init__(
address: str = DEFAULT_SYSLOG_PATH,
pending_queue: deque | None = None
):
"""
address - path to Unix socket (should be defined in syslog-ng config)
pending_queue - deque object that will be used for storing failed
LogRecords if syslog is currently down.
Note: maxlen should be set unless one wants to queue the log messages
without loss until syslog connection restored. This is probably
desireable for auditing, but not for general purposes (where it's
better to just specify a fallback handler).
"""
self.pending_queue = pending_queue
super().__init__(address, socktype=socket.SOCK_STREAM)

def drain_pending_queue(self):
def drain_pending_queue(self) -> bool:
"""
Attempt to emit any log records that have been queued up due to logging
failures to the syslog socket.
Returns:
True if successfully drained entire queue else False
Raises:
Should not raise exceptions
"""
while self.pending_queue:
record = self.pending_queue.popleft()
try:
Expand All @@ -138,7 +159,19 @@ def drain_pending_queue(self):

return True

def fallback(self, record):
def fallback(self, record: logging.LogRecord) -> None:
"""
Fallback logging mechanism in case the syslog target is down.
In this case we emit the log record to the fallback handler and ignore
any errors.
Returns:
None
Raises:
Should not raise exceptions
"""
if not self.fallback_handler:
return

Expand All @@ -147,7 +180,13 @@ def fallback(self, record):
except Exception:
pass

def emit(self, record):
def emit(self, record: logging.LogRecord) -> None:
"""
Emit a LogRecord to syslog. If this fails then add to pending queue and
emit via our fallback handler.
"""

# First attempt to drain the pending queue
if not self.drain_pending_queue():
# Failed to drain our pending queue so add this record to the
# ever-growing deque
Expand All @@ -163,19 +202,33 @@ def emit(self, record):
self.pending_queue.append(record)
self.fallback(record)

def handleError(self, record):
def handleError(self, record: logging.LogRecord) -> None:
"""
Override the default syslog error handler if we have a pending_queue to
defined. Exception raised here passes back up to the the emit() call.
"""
# re-raise it back up to the emit call
if self.pending_queue is None:
return super().handleError(record)

raise

def set_fallback_handler(self, fallback: logging.Handler):
def set_fallback_handler(self, fallback: logging.Handler) -> None:
""" Set a fallback handler (for example to file) that will be used if syslog socket logging fails """
if not isinstance(fallback, logging.Handler):
raise TypeError(f'{fallback}: not a logging.Handler')

self.fallback_handler = fallback

def close(self) -> None:
# Close our socket
super().close()

if self.fallback_handler:
# close any open file handler
self.fallback_handler.close()
self.fallback_handler = None


def setup_syslog_handler(tnlog: TNLog, fallback: logging.Handler | None) -> logging.Logger:
# Use `QueueHandler` to avoid blocking IO in asyncio main loop
Expand Down

0 comments on commit 0c5711d

Please sign in to comment.