Python for AIPython 3.12 · Pydantic 2.12
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
43 small wins to finish your pathNext lesson →

Logging

Logging is the standard-library way to record what a program did, at levels from debug to critical, written to the console or a file.

Last updated: 13 Oct, 2026 · Python 3.12

The tests in pytest.raises and parametrize checked the code before it ships. Logging records what the code does while it runs. Up to now every program used print; print cannot say how important a line is, be turned down to warnings only, or be sent to a file without changing the code. The logging module, part of Python, does all three.

Syntax:

python
import logging

logging.basicConfig(level=logging.INFO)   # show INFO and above
logging.info("a normal event")            # debug, info, warning, error, critical

The five levels

The levels go from least to most serious. Each one is a function on logging:

python
logging.debug("smallest detail, for finding a bug")
logging.info("a normal event")
logging.warning("something to look at")
logging.error("something failed")
logging.critical("the program cannot go on")

Setting the level with basicConfig

Example
import logging
import sys

logging.basicConfig(level=logging.INFO, stream=sys.stdout,
                    format="%(levelname)s %(message)s")

logging.debug("connecting to the model")
logging.info("loaded 5 tickets")
logging.warning("ticket 4 needs a person")

basicConfig sets logging up once. level=logging.INFO is a floor: INFO and above appear, so the debug line is dropped. format controls each line; here it shows the level and the message. stream=sys.stdout sends it to the screen, which is also where logging goes by default.

Naming a logger

Example
import logging
import sys

logging.basicConfig(level=logging.INFO, stream=sys.stdout,
                    format="%(name)s %(levelname)s %(message)s")

logger = logging.getLogger("triage")
logger.info("ticket 1 sorted as billing")
logger.warning("ticket 4 needs a person")

logging.getLogger("triage") makes a named logger, and %(name)s prints that name on every line. In a program with several parts you can see which part wrote each line, and turn one part's logging up or down on its own. Every library names its own logger this way.

Logging to a file

Example
import logging

logging.basicConfig(filename="run.log", filemode="w", level=logging.INFO,
                    format="%(name)s %(levelname)s %(message)s")

logging.getLogger("triage").info("sorted 5 tickets")
logging.getLogger("triage").warning("ticket 4 needs a person")

print(open("run.log").read())

filename sends the log to a file instead of the screen, and filemode="w" starts it fresh each run, where "a" would add to the end. Reading the file back shows the two lines. A running service keeps its log in a file so you can read what happened after the fact.

Adding a timestamp

Two fields turn each line into a dated record:

python
logging.basicConfig(
    filename="run.log", filemode="w", level=logging.INFO,
    format="%(asctime)s %(name)s %(levelname)s %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

%(asctime)s adds the time to each line and datefmt sets how it looks, so a line reads 2026-10-13 09:30:01 triage INFO sorted 5 tickets.

printlogging
LevelsOne, always showndebug to critical, filtered by level
Where it goesThe screenScreen or file, set in one place
In a libraryNoise you cannot turn offA named logger you can turn down

Where logging shows up in AI code

  • Recording each model call, its tokens and whether it was retried, to a file you can read later.
  • Turning a noisy library down to warnings while keeping your own logger at info.
  • Separating normal events (info) from failures (error) so an alert can watch for errors only.
Watch out. basicConfig only does something the first time it runs; a second call with new settings is ignored. Set it once, early, before any logging happens.
Try it yourself
  • Change level=logging.INFO to logging.DEBUG in the named-logger example and run it again.
  • Call logging.getLogger("noisy").setLevel(logging.WARNING), then an info on it, and see that nothing prints.
  • Change filemode="w" to "a", run the file example twice, and read run.log.

Little by little, you're building something great.