Programming with AI
ENRU
4.2

Running it and reading the logs

what the program tells you about itself

What it is

A log is what a program writes about itself while it runs. For debugging it matters more than a traceback: a traceback shows the moment of the crash, a log shows what was happening before.

For a bot or a server the log is the only way to find out what happened at all: you don't see the user's screen and you can't ask them again.

What it looks like in code

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s %(name)s: %(message)s",
)
log = logging.getLogger(__name__)


def fetch_weather(city: str) -> dict:
    log.info("weather request: %s", city)
    try:
        data = call_api(city)
    except TimeoutError:
        log.warning("timeout for city %s, retrying", city)
        data = call_api(city)
    log.debug("API response: %s", data)
    return data

Output:

measured
2026-10-14 13:02:11 INFO handlers.weather: weather request: Oslo
2026-10-14 13:02:12 WARNING handlers.weather: timeout for city Oslo, retrying

Five levels

LevelWhenVisible by default
DEBUGdetails for debuggingno
INFOthe normal course of workyes
WARNINGodd, but we carry onyes
ERRORwe failed to do what was askedyes
CRITICALwe cannot carry onyes

The line level=logging.INFO means: show INFO and anything more serious. To see DEBUG, the level is changed to logging.DEBUG — usually temporarily, while hunting an error.

How to read a log

Bottom to top, up to the first surprise. The last lines are consequences; the interesting part is usually above.

Look at the times. A gap of several seconds between neighbouring lines shows where the program was waiting. It is the cheapest way to find out what is slow.

Look for a WARNING before an ERROR. The warning often names the real cause, while the error is only its consequence.

Count the repeats. The same line a hundred times in a row is a loop that should not have looped, or retries that never end.

printlogging
in a single script, while you debugin a bot, a server, anything long-running
disappears when the program isn't in a terminalgoes to a file, survives a restart
no levels and no timestampslevels, time, module name

The rule: while you are hunting an error by hand — print (article 4.5). Anything that stays in the code — logging.

What to tell the AI

Prompt
Add logging to this function: INFO on entry with the key
arguments, WARNING for abnormal situations, ERROR on failure.
Don't log tokens or personal data.

When the log already exists and you need to make sense of it:

Prompt
Here is a chunk of the log. What happened here? What should
I look at first?

<log>

When there isn't enough logging:

Prompt
I don't understand why this function returns an empty list.
Where should I add logging to make it visible?
As few lines as possible.

What to look for

Secrets in the logs. A token or password written to the log is the same secret in plain sight, only in a file that then gets forwarded. Check what is being logged before handing a log to anyone, the AI included.

A log with nothing in it. If the log is empty when an error occurs, logging was forgotten — and the only way to learn the cause is already gone.

A log with everything in it. The opposite extreme: DEBUG on everything in production. The file grows, the important parts drown, the disk fills.

A message with no data. log.error("error") says nothing. A good entry holds values: which city, which user, which response code.

Logging inside a loop over a thousand items. A thousand lines for one request. Log the outcome, not every step.

Cheat sheet

  • The log matters more than the traceback: it shows what came before.

  • Five levels: DEBUG · INFO · WARNING · ERROR · CRITICAL.

  • Read bottom to top until the first surprise; watch the time gaps.

  • A WARNING before an ERROR is often the real cause.

  • print while debugging, logging for anything that stays in the code.

  • Always include values in the message. Never log secrets.