Skip to content

allow multiple handlers per logger #44

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 12, 2022
Merged
Changes from 6 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
67 changes: 51 additions & 16 deletions adafruit_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,8 @@
.. note::

This module has a few key differences compared to its CPython counterpart, notably
that loggers can only be assigned one handler at a time. Calling ``addHander()``
replaces the currently stored handler for that logger. Additionally, the default
formatting for handlers is different.
that loggers do not form a hierarchy that allows record propagation.
Additionally, the default formatting for handlers is different.

Attributes
----------
Expand Down Expand Up @@ -141,13 +140,26 @@ def _level_for(value: int) -> str:
- ``args`` - The additional positional arguments provided
"""

# Whether to print exceptions caught during handler's emit().
printHandlerExceptions = True


def _logRecordFactory(name, level, msg, args):
return LogRecord(name, level, _level_for(level), msg, time.monotonic(), args)


class Handler:
"""Abstract logging message handler."""
"""Base logging message handler."""

def __init__(self, level: int = NOTSET) -> None:
"""Create Handler instance"""
self.level = level

def setLevel(self, level: int) -> None:
"""
Set the logging level of this handler.
"""
self.level = level

# pylint: disable=no-self-use
def format(self, record: LogRecord) -> str:
Expand All @@ -156,9 +168,7 @@ def format(self, record: LogRecord) -> str:
:param record: The record (message object) to be logged
"""

return "{0:<0.3f}: {1} - {2}".format(
record.created, record.levelname, record.msg
)
return f"{record.created:<0.3f}: {record.levelname} - {record.msg}"

def emit(self, record: LogRecord) -> None:
"""Send a message where it should go.
Expand Down Expand Up @@ -277,7 +287,8 @@ def __init__(self, name: Hashable, level: int = WARNING) -> None:
self.name = name
"""The name of the logger, this should be unique for proper
functionality of `getLogger()`"""
self._handler = None
self._handlers = []
self.emittedNoHandlerWarning = False

def setLevel(self, log_level: int) -> None:
"""Set the logging cutoff level.
Expand All @@ -296,23 +307,47 @@ def getEffectiveLevel(self) -> int:
return self._level

def addHandler(self, hdlr: Handler) -> None:
"""Sets the handler of this logger to the specified handler.

*NOTE* This is slightly different from the CPython equivalent
which adds the handler rather than replacing it.
"""Adds the handler to this logger.

:param Handler hdlr: The handler to add
"""
self._handler = hdlr
self._handlers.append(hdlr)

def removeHandler(self, hdlr: Handler) -> None:
"""Remove handler from this logger.

:param Handler hdlr: The handler to remove
"""
self._handlers.remove(hdlr)

def hasHandlers(self) -> bool:
"""Whether any handlers have been set for this logger"""
return self._handler is not None
return len(self._handlers) > 0

def _log(self, level: int, msg: str, *args) -> None:
record = _logRecordFactory(self.name, level, msg % args, args)
if self._handler and level >= self._level:
self._handler.emit(record)
self.handle(record)

def handle(self, record: LogRecord) -> None:
"""Pass the record to all handlers registered with this logger.

:param LogRecord record: log record
"""
if not self.hasHandlers() and not self.emittedNoHandlerWarning:
sys.stderr.write(f"Logger '{self.name}' has no handlers\n")
self.emittedNoHandlerWarning = True
return

if record.levelno >= self._level:
for handler in self._handlers:
if record.levelno >= handler.level:
try:
handler.emit(record)
except Exception as e: # pylint: disable=broad-except
if sys.stderr and printHandlerExceptions:
sys.stderr.write(
f"Handler {handler} produced exception: {e}\n"
)

def log(self, level: int, msg: str, *args) -> None:
"""Log a message.
Expand Down