Skip to content
Foxipa

Suggest an edit

Tell us what should be corrected, clarified, or improved. Submission will be connected to Foxipa’s contribution service later.

No information is sent anywhere yet. Your suggestion is only prepared in your browser.

Learning

Logging Events with Python's logging Module

Record useful runtime events with named loggers, severity levels, handlers, formatters, and exception details.

Levels: Intermediate ~22 min read

Intermediate

Use print() for ordinary output meant for a person using a program. Use logging when a program should record events with severity and context so developers or operators can understand what happened later. Raise an exception when an operation cannot continue normally; use warnings.warn() for certain avoidable API-use problems that a caller should fix. Logging complements these tools rather than replacing them.

Start with a named logger and useful levels

A logger creates records about events. The common levels, from least to most severe, are DEBUG, INFO, WARNING, ERROR, and CRITICAL. The default root logger threshold is WARNING, so DEBUG and INFO records are normally hidden until logging is configured. In an application entry point, call basicConfig() before the first logging call; in a module, prefer a named logger from getLogger(__name__) so the application can decide where messages go.

Interactive Runnable example
Edit

Ready to run.

Quick check

Which tool is the better fit for recording a normal runtime event that may be useful during troubleshooting?

import logging
logger = logging.getLogger("demo")
logger.info("Import complete")

Choose a threshold that matches the task

Set a logger's level to decide which severities it processes. INFO includes INFO, WARNING, ERROR, and CRITICAL while excluding DEBUG. Logger levels and handler levels work together: a record must pass the logger's threshold and then the handler's threshold to be emitted. If a non-root logger has no explicit level, it inherits an effective level through its ancestors.

Interactive Runnable example
Edit

Ready to run.

Quick check

When a logger is set to INFO, which of these records normally passes its level threshold?

import logging
print(logging.INFO > logging.DEBUG)

Pass variable data as logging arguments

Pass a message template and its values as separate arguments, for example logger.info("Loaded %d rows from %s", row_count, source). The logging package combines these values when the record needs to be formatted. This avoids building an f-string before the logger decides whether the record will be handled. The percent-style placeholders in a logging call are separate from the format string used to lay out the final log record.

Interactive Runnable example
Edit

Ready to run.

Quick check

Which call lets logging defer interpolation until the message is processed?

import logging
logger = logging.getLogger("demo")
count = 7
logger.info("Processed %d items", count)

Understand loggers, handlers, and formatters

A logger is the interface your code calls. A handler sends records to a destination, such as a stream or file. A formatter controls how each record is displayed. For simple scripts, basicConfig() can set the root logger's level, destination, and format. In larger applications, logger names commonly follow the module path with getLogger(__name__); libraries should usually leave application-wide logging configuration to the application.

Interactive Runnable example
Edit

Ready to run.

Quick check

Which logging component decides the layout of a record such as "INFO:worker:Started"?

import io
import logging
stream = io.StringIO()
handler = logging.StreamHandler(stream)
handler.setFormatter(logging.Formatter("%(levelname)s:%(message)s"))
record = logging.LogRecord("worker", logging.INFO, "example.py", 1, "Started", (), None)
handler.handle(record)
print(stream.getvalue(), end="")
handler.close()

Include exception information when handling a failure

Inside an except block, logger.exception() records a message at ERROR level and includes the active exception traceback. Use it when the application is handling the failure and needs to preserve diagnostic context. Logging does not replace raising an exception when the caller must be told that the operation failed.

Interactive Runnable example
Edit

Ready to run.

Quick check

Where should logger.exception() normally be called to include the active traceback?

import logging
try:
    int("x")
except ValueError:
    logging.getLogger(__name__).exception("Could not parse input")

Send log records to a file safely

For file output, attach a FileHandler or use the filename option of basicConfig() in a simple application. When creating a handler directly, remove it from the logger and close it when it is no longer needed. The example uses a temporary directory so running it does not leave a log file in the project. Configure logging once at the application boundary instead of repeatedly changing global logging configuration inside reusable functions.

Interactive Runnable example
Edit

Ready to run.

Key takeaways

  • Use print() for ordinary user-facing output, logging for runtime records, warnings.warn() for appropriate caller-fixable API warnings, and exceptions when failure must be communicated to a caller.
  • Choose DEBUG, INFO, WARNING, ERROR, or CRITICAL based on the event severity; levels act as thresholds.
  • Use getLogger(__name__) in modules and configure destinations at the application boundary.
  • Log variable values as separate arguments so interpolation can be deferred, and use logger.exception() inside an except block when a traceback is needed.
  • Loggers create records, handlers route them, and formatters control their layout; close handlers your code creates.
Was this page helpful?

Mark this learning resource complete to track your learning progress.