Attach a named logger without configuring the root logger
Also published in our Blogger archive.
A named logger can serve a single component without calling logging.basicConfig() or adding a handler to root. This example snapshots the root logger's handlers, creates the named logger service.worker, and attaches an in-memory StreamHandler only to that named logger. The handler's format is intentionally compact and uses a fixed warning message.
propagate=False keeps this record from also being offered to root handlers. After logging retry=0, the example asserts the exact text captured in its private StringIO and asserts that the root handler tuple is unchanged from the earlier snapshot. It then prints the captured line, producing WARNING service.worker retry=0 with one final newline.
The assertion establishes only that this code did not change root handlers during this run. It does not configure root globally, silence other named loggers, or prove that another thread will not change logging configuration. In an application that can initialize the same component more than once, track handler ownership and use addHandler() and removeHandler() for lifecycle management; directly changing Logger.handlers is not the supported pattern. If the application instead wants centralized handling, it can configure root once and allow child loggers to propagate.
getLogger() documents named logger lookup, and Logger.addHandler() documents handler attachment. These APIs are available in supported Python 3 versions.
AI assistance disclosure: This article was drafted with AI assistance and should be adapted to the application's logging policy.
import io
import logging
root = logging.getLogger()
root_handlers_before = tuple(root.handlers)
stream = io.StringIO()
handler = logging.StreamHandler(stream)
handler.setFormatter(logging.Formatter("%(levelname)s %(name)s %(message)s"))
logger = logging.getLogger("service.worker")
logger.propagate = False
logger.setLevel(logging.WARNING)
logger.addHandler(handler)
logger.warning("retry=%d", 0)
record = stream.getvalue()
assert record == "WARNING service.worker retry=0\n"
assert tuple(root.handlers) == root_handlers_before
print(record, end="")
WARNING service.worker retry=0