Skip to main content

Lib/logging/__init__.py (part 5)

Source:

cpython 3.14 @ ab2d84fe1023/Lib/logging/__init__.py

This annotation covers the handler hierarchy. See lib_logging4_detail for Logger, LogRecord, and the filter chain.

Map

LinesSymbolRole
1-80Handler.emitBase emit — subclasses override this
81-160StreamHandlerWrite formatted records to a stream
161-240FileHandlerWrite to a file; open/close management
241-320NullHandlerDo-nothing handler for library authors
321-500Handler.handleAcquire lock, call emit, release lock

Reading

Handler.emit

# CPython: Lib/logging/__init__.py:940 Handler.emit
def emit(self, record):
"""
Do whatever it takes to actually log the specified logging record.
This version is intended to be implemented by subclasses and so
raises a NotImplementedError.
"""
raise NotImplementedError('emit must be implemented by Handler subclasses')

emit is the abstract hook. All output handlers override this. The base Handler.handle method acquires a lock, calls self.emit, and releases the lock.

StreamHandler

# CPython: Lib/logging/__init__.py:1060 StreamHandler.emit
def emit(self, record):
try:
msg = self.format(record)
stream = self.stream
stream.write(msg + self.terminator)
self.flush()
except RecursionError:
raise
except Exception:
self.handleError(record)

StreamHandler catches all exceptions from format and write (except RecursionError) and calls handleError, which by default prints to sys.stderr. This ensures logging never crashes the application.

Handler.handle

# CPython: Lib/logging/__init__.py:920 Handler.handle
def handle(self, record):
rv = self.filter(record)
if rv:
self.acquire()
try:
self.emit(record)
finally:
self.release()
return rv

The lock is a threading.RLock so handlers can be called from multiple threads safely. filter(record) returns the record (or a modified version) if it passes all filters, or False to suppress it.

FileHandler

# CPython: Lib/logging/__init__.py:1100 FileHandler.__init__
class FileHandler(StreamHandler):
def __init__(self, filename, mode='a', encoding=None, delay=False,
errors=None):
self.baseFilename = os.path.abspath(filename)
self.mode = mode
self.encoding = encoding
self.errors = errors
self.delay = delay
if delay:
self.stream = None
else:
StreamHandler.__init__(self, self._open())

def emit(self, record):
if self.stream is None:
self.stream = self._open()
StreamHandler.emit(self, record)

delay=True defers file creation until the first log message. This is useful for handlers at the WARNING level: if no warnings fire, no file is created.

NullHandler

# CPython: Lib/logging/__init__.py:1200 NullHandler
class NullHandler(Handler):
def handle(self, record):
pass
def emit(self, record):
pass
def createLock(self):
self.lock = None
def acquire(self):
pass
def release(self):
pass

Library authors add logging.getLogger(__name__).addHandler(NullHandler()) to prevent "No handlers could be found" warnings when the library is used without any logging configuration.

gopy notes

StreamHandler is module/logging.StreamHandler in module/logging/module.go. emit writes to the stream object via objects.Write. The lock is a Go sync.Mutex. FileHandler opens via os.OpenFile.