Skip to content

Commit ebf01e1

Browse files
authored
Merge pull request #44 from vladak/multiple_handlers
allow multiple handlers per logger
2 parents eb70acf + 54ad091 commit ebf01e1

File tree

2 files changed

+75
-25
lines changed

2 files changed

+75
-25
lines changed

adafruit_logging.py

Lines changed: 58 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,8 @@
2525
.. note::
2626
2727
This module has a few key differences compared to its CPython counterpart, notably
28-
that loggers can only be assigned one handler at a time. Calling ``addHander()``
29-
replaces the currently stored handler for that logger. Additionally, the default
30-
formatting for handlers is different.
28+
that loggers do not form a hierarchy that allows record propagation.
29+
Additionally, the default formatting for handlers is different.
3130
3231
Attributes
3332
----------
@@ -147,7 +146,17 @@ def _logRecordFactory(name, level, msg, args):
147146

148147

149148
class Handler:
150-
"""Abstract logging message handler."""
149+
"""Base logging message handler."""
150+
151+
def __init__(self, level: int = NOTSET) -> None:
152+
"""Create Handler instance"""
153+
self.level = level
154+
155+
def setLevel(self, level: int) -> None:
156+
"""
157+
Set the logging level of this handler.
158+
"""
159+
self.level = level
151160

152161
# pylint: disable=no-self-use
153162
def format(self, record: LogRecord) -> str:
@@ -156,9 +165,7 @@ def format(self, record: LogRecord) -> str:
156165
:param record: The record (message object) to be logged
157166
"""
158167

159-
return "{0:<0.3f}: {1} - {2}".format(
160-
record.created, record.levelname, record.msg
161-
)
168+
return f"{record.created:<0.3f}: {record.levelname} - {record.msg}"
162169

163170
def emit(self, record: LogRecord) -> None:
164171
"""Send a message where it should go.
@@ -239,13 +246,13 @@ def emit(self, record: LogRecord) -> None:
239246

240247

241248
logger_cache = {}
249+
_default_handler = StreamHandler()
242250

243251

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

251258

@@ -277,7 +284,8 @@ def __init__(self, name: Hashable, level: int = WARNING) -> None:
277284
self.name = name
278285
"""The name of the logger, this should be unique for proper
279286
functionality of `getLogger()`"""
280-
self._handler = None
287+
self._handlers = []
288+
self.emittedNoHandlerWarning = False
281289

282290
def setLevel(self, log_level: int) -> None:
283291
"""Set the logging cutoff level.
@@ -296,23 +304,56 @@ def getEffectiveLevel(self) -> int:
296304
return self._level
297305

298306
def addHandler(self, hdlr: Handler) -> None:
299-
"""Sets the handler of this logger to the specified handler.
300-
301-
*NOTE* This is slightly different from the CPython equivalent
302-
which adds the handler rather than replacing it.
307+
"""Adds the handler to this logger.
303308
304309
:param Handler hdlr: The handler to add
305310
"""
306-
self._handler = hdlr
311+
self._handlers.append(hdlr)
312+
313+
def removeHandler(self, hdlr: Handler) -> None:
314+
"""Remove handler from this logger.
315+
316+
:param Handler hdlr: The handler to remove
317+
"""
318+
self._handlers.remove(hdlr)
307319

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

312324
def _log(self, level: int, msg: str, *args) -> None:
313325
record = _logRecordFactory(self.name, level, msg % args, args)
314-
if self._handler and level >= self._level:
315-
self._handler.emit(record)
326+
self.handle(record)
327+
328+
def handle(self, record: LogRecord) -> None:
329+
"""Pass the record to all handlers registered with this logger.
330+
331+
:param LogRecord record: log record
332+
"""
333+
if (
334+
_default_handler is None
335+
and not self.hasHandlers()
336+
and not self.emittedNoHandlerWarning
337+
):
338+
sys.stderr.write(
339+
f"Logger '{self.name}' has no handlers and default handler is None\n"
340+
)
341+
self.emittedNoHandlerWarning = True
342+
return
343+
344+
emitted = False
345+
if record.levelno >= self._level:
346+
for handler in self._handlers:
347+
if record.levelno >= handler.level:
348+
handler.emit(record)
349+
emitted = True
350+
351+
if (
352+
not emitted
353+
and _default_handler
354+
and record.levelno >= _default_handler.level
355+
):
356+
_default_handler.emit(record)
316357

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

examples/logging_simpletest.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,31 @@
88

99
import adafruit_logging as logging
1010

11-
# This should produce an error output
11+
# This should produce an info output via default handler.
12+
13+
logger_default_handler = logging.getLogger("default_handler")
14+
logger_default_handler.setLevel(logging.INFO)
15+
logger_default_handler.info("Default handler: Info message")
16+
assert not logger_default_handler.hasHandlers()
17+
18+
# This should produce an error output via Stream Handler.
1219

1320
logger = logging.getLogger("test")
14-
print_logger = logging.StreamHandler()
15-
logger.addHandler(print_logger)
21+
print_handler = logging.StreamHandler()
22+
logger.addHandler(print_handler)
23+
assert logger.hasHandlers()
1624

1725
logger.setLevel(logging.ERROR)
18-
logger.info("Info message")
19-
logger.error("Error message")
26+
logger.info("Stream Handler: Info message")
27+
logger.error("Stream Handler: Error message")
2028

21-
# This should produce no output
29+
# This should produce no output at all.
2230

2331
null_logger = logging.getLogger("null")
2432
null_handler = logging.NullHandler()
2533
null_logger.addHandler(null_handler)
34+
assert null_logger.hasHandlers()
2635

2736
null_logger.setLevel(logging.ERROR)
28-
null_logger.info("Info message")
29-
null_logger.error("Error message")
37+
null_logger.info("Null Handler: Info message")
38+
null_logger.error("Null Handler: Error message")

0 commit comments

Comments
 (0)