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
3231Attributes
3332----------
@@ -147,7 +146,17 @@ def _logRecordFactory(name, level, msg, args):
147146
148147
149148class 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
241248logger_cache = {}
249+ _default_handler = StreamHandler ()
242250
243251
244252def _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.
0 commit comments