We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Config, Logs and Untrusted Input step 11 of 16
Log from a Library the Right Way
Your team's shop package has an inventory module. A web shop uses it
and wants only warnings in its logs. A nightly report job uses it too and
wants every detail while someone debugs it. If the library prints, or sets
up logging itself, it chooses for both of them. Instead, a library sends
its messages to a named logger and lets each application decide what to
show.
import logging
logger = logging.getLogger("shop.inventory")
logger.debug("reserving %d of %s", 3, "apples") # hidden unless the app asks for DEBUG
logger.warning("%s is low: %d left", "pears", 4)
Logger names form a tree by their dots. shop.inventory is a child of
shop, and shop is a child of the root logger. Each logging call
creates a record. A record logged on shop.inventory travels up the
tree, and every handler on the way receives it. A handler is the
object that decides where records go: a file, the terminal, or a list in
a test. A logger with no level of its own uses the level of its nearest
ancestor that has one.
What to write
Fill in reserve(stock, item, quantity), library code for the
shop.inventory module.
- At module level, create the logger once with
logging.getLogger("shop.inventory"). - On every call, log at DEBUG:
"reserving %d of %s", withquantityanditemas arguments. - Take
quantityaway fromstock[item]. If fewer thanLOW_STOCKare left, log at WARNING:"%s is low: %d left", withitemand the number left. - Return the number left.
- Pass the values as arguments after the message, not inside an f-string. The logger then builds the text only if a handler needs it.
- Do not add handlers, set levels, or call
logging.basicConfig()orlogging.warning(). Those choices belong to the application.
In a real module you would write logging.getLogger(__name__), and Python
fills in the module's dotted name. The grader runs your code without a
module name, so __name__ there is "builtins". Write the name out.
Edge cases
- Exactly
LOW_STOCKleft is not low. Warn only below it. - No test asks for more than is in stock, so you do not need to handle running out. Taking all of an item leaves 0, which is low.
What the driver reports
solve plays the application. It adds a handler to the shop logger and
sets that logger's level. The handler keeps every record in a list. Then
solve makes the calls and removes everything it added. It returns:
lines: each record that got through, asLEVEL logger: message.values_as_args:Trueif every record kept its values as arguments instead of a ready-made string.library_handlersandlibrary_level: the number of handlers and the level set on theshop.inventorylogger itself. A library should leave these as0and"NOTSET".root_handlers_added: the number of handlers that appeared on the root logger.logging.basicConfig()adds one, and so does a call such aslogging.warning()when nothing is set up. A library should add none.stock: what is left of each item.
Going further
A library can add logging.NullHandler() to its top-level logger, here
shop. Records still travel up to the application's handlers, and the
library's warnings no longer print on their own when the application has
set up no logging at all.
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.