We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Config, Logs and Untrusted Input step 12 of 16
Trace a Log Record Up the Logger Tree
In Log from a Library the Right Way, your library logged through
shop.inventory. The handler that the application put on shop still
received the records. A handler is the object that writes a record
somewhere, such as the terminal or a file. Two walks up the logger tree explain
why that worked, and in this exercise you write both.
Loggers form a tree by name. shop is the parent of shop.inventory, and the
root logger is the parent of shop. When code calls logger.info(...):
- The logger compares the record's level with its effective level. That is the level set on the logger itself or, if it has none, on its nearest ancestor that has one. The logger drops a record below that level.
- The record goes to the logger's own handlers, then to its parent's, and so
on up to root. This is propagation. A logger whose
propagateattribute isFalseis the last one whose handlers get the record.
Only the first logger checks its level. If you set shop.inventory to DEBUG
and shop to WARNING, a debug record from shop.inventory still reaches
the handler on shop.
This exercise models the tree with plain dictionaries instead of calling the
logging module, so the tests can check each walk on its own.
logger_ancestry("acme.db.pool", {"acme.db": False}) # ["acme.db.pool", "acme.db"]
effective_level({"acme": 20}, "acme.db.pool") # 20
What to write
def logger_ancestry(name: str, propagate: Mapping[str, bool]) -> list[str]: ...
def effective_level(configured: Mapping[str, int], name: str) -> int: ...
solve returns both results, under "ancestry" and "effective". Keep it as
it is. Your code must pass mypy --strict.
Both functions take a logger name. "" and "root" both mean the root
logger. Any other name is a dotted name, and every prefix of it counts as a
logger. So "acme.db.pool" has this chain, nearest first:
acme.db.pool -> acme.db -> acme -> root
Root comes once, at the end of every chain. That holds even for a name such as
"root.worker", whose chain is root.worker, then root. In both mappings,
root's key is "root", never "". Ignore entries for loggers outside the
chain, and do not change the mappings.
Real logging creates only the loggers that code asks for with getLogger. A
missing one would have no level and no handlers, so counting every prefix
changes neither walk's result.
Whose handlers get the record
logger_ancestry returns the loggers whose handlers receive the record, in
order. Start with name and walk toward root. Stop after the first logger
whose propagate flag is False, and include that logger. A logger missing
from propagate has the flag True. Root's flag never matters, because root
has no parent. Each handler on those loggers can still drop a record that is
below the handler's own level.
Which level applies
effective_level walks the full chain, nearest first, and returns the first
level that is not NOTSET (0). A logger missing from configured counts as
NOTSET. Root works differently:
- If
configuredhas no"root"entry, returnWARNING(30), the level root has when nothing configures it. - If root has an entry, return it, even when it is
0.
So a missing root and a root set to 0 give different answers:
configured |
effective_level(configured, "acme.db") |
|---|---|
{} |
30 |
{"root": 0} |
0 |
{"acme": 20, "root": 0} |
20 |
The propagate flags do not affect the level. A logger with propagate set to
False still takes its level from its parent, so walk the full chain, not
the list from logger_ancestry.
Root's default of WARNING is why an unconfigured library's logger.info(...)
prints nothing.
Going further
Three more settings can stop a record, and this exercise leaves them out.
They are a logger's disabled flag, logging.disable(), and filters. See the
logging reference.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Python reference solution
Sign in to track your attempts toward unlocking the reference solution.