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
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
75 changes: 58 additions & 17 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 @@ -147,7 +146,17 @@ def _logRecordFactory(name, level, msg, 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 +165,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 @@ -239,13 +246,13 @@ def emit(self, record: LogRecord) -> None:


logger_cache = {}
_default_handler = StreamHandler()


def _addLogger(logger_name: Hashable) -> None:
"""Adds the logger if it doesn't already exist"""
if logger_name not in logger_cache:
new_logger = Logger(logger_name)
new_logger.addHandler(StreamHandler())
logger_cache[logger_name] = new_logger


Expand Down Expand Up @@ -277,7 +284,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 +304,56 @@ 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 (
_default_handler is None
and not self.hasHandlers()
and not self.emittedNoHandlerWarning
):
sys.stderr.write(
f"Logger '{self.name}' has no handlers and default handler is None\n"
)
self.emittedNoHandlerWarning = True
return

emitted = False
if record.levelno >= self._level:
for handler in self._handlers:
if record.levelno >= handler.level:
handler.emit(record)
emitted = True

if (
not emitted
and _default_handler
and record.levelno >= _default_handler.level
):
_default_handler.emit(record)

def log(self, level: int, msg: str, *args) -> None:
"""Log a message.
Expand Down
25 changes: 17 additions & 8 deletions examples/logging_simpletest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,31 @@

import adafruit_logging as logging

# This should produce an error output
# This should produce an info output via default handler.

logger_default_handler = logging.getLogger("default_handler")
logger_default_handler.setLevel(logging.INFO)
logger_default_handler.info("Default handler: Info message")
assert not logger_default_handler.hasHandlers()

# This should produce an error output via Stream Handler.

logger = logging.getLogger("test")
print_logger = logging.StreamHandler()
logger.addHandler(print_logger)
print_handler = logging.StreamHandler()
logger.addHandler(print_handler)
assert logger.hasHandlers()

logger.setLevel(logging.ERROR)
logger.info("Info message")
logger.error("Error message")
logger.info("Stream Handler: Info message")
logger.error("Stream Handler: Error message")

# This should produce no output
# This should produce no output at all.

null_logger = logging.getLogger("null")
null_handler = logging.NullHandler()
null_logger.addHandler(null_handler)
assert null_logger.hasHandlers()

null_logger.setLevel(logging.ERROR)
null_logger.info("Info message")
null_logger.error("Error message")
null_logger.info("Null Handler: Info message")
null_logger.error("Null Handler: Error message")