Logging Module

Logging is used to record events that happen when a program runs. These events may include information messages, warnings, errors, and debugging details. Logging helps developers understand the flow of execution, track issues, and maintain applications. It is a better alternative to using print() because logging provides levels, formatting, timestamps, and can store logs in files.

Python’s built-in logging module provides a flexible framework for adding log messages to programs.

1. Why Logging Is Needed

The logging module is essential for:

  • Monitoring program execution
  • Debugging without interrupting the user
  • Recording errors and warnings
  • Saving logs for future analysis
  • Building production-level applications

Logs help identify what happened and when it happened, especially in large applications.

2. Basic Logging Setup

The simplest way to log messages is by calling the logging functions directly.

import logging

logging.warning("This is a warning")
logging.error("This is an error")
logging.info("Information message")

Output:

WARNING:root:This is a warning
ERROR:root:This is an error

By default, only WARNING and above are shown. INFO is ignored unless the configuration changes.

3. Logging Levels

Python defines multiple levels of severity for log messages.

LevelDescription
DEBUGDetailed information for debugging
INFO                         General events and progress
WARNINGSomething unexpected but recoverable
ERRORSerious issue; function failed
CRITICALSevere error; program may stop

Higher numbers represent higher severity.

4. Configuring Logging Output

To show all logs, including INFO and DEBUG, configure the logging level.

import logging

logging.basicConfig(level=logging.DEBUG)
logging.debug("Debug message")
logging.info("Information")
logging.warning("Warning")

Output:

DEBUG:root:Debug message
INFO:root:Information
WARNING:root:Warning

basicConfig() must be called before logging messages.

5. Adding Formatting to Logs

Custom formatting makes logs more readable.

import logging logging.basicConfig(    format="%(asctime)s - %(levelname)s - %(message)s",    level=logging.INFO ) import logging

logging.basicConfig(
   format="%(asctime)s - %(levelname)s - %(message)s",
   level=logging.INFO
)

logging.info("Application started")

Output:

2025-03-30 11:24:10,245 - INFO - Application started

Common Format Variables

VariableMeaning
%(asctime)sTimestamp
%(levelname)s     Log level
%(message)sLog message
%(name)sLogger name
%(lineno)dLine number

6. Logging to a File

Instead of printing logs to the console, logs can be stored in a file.

import logging

logging.basicConfig(
   filename="app.log",
   level=logging.INFO,
   format="%(asctime)s - %(levelname)s - %(message)s"
)

logging.info("Logging to a file")

Output inside app.log:

2025-03-30 11:30:15,450 - INFO - Logging to a file

This is commonly used in production systems.

7. Creating a Custom Logger

Custom loggers allow more control, especially in large applications.

import logging

logger = logging.getLogger("myapp")
logger.setLevel(logging.DEBUG)

handler = logging.FileHandler("myapp.log")
formatter = logging.Formatter("%(levelname)s - %(message)s")
handler.setFormatter(formatter)

logger.addHandler(handler)

logger.debug("Debug message from custom logger")
logger.error("Error occurred")

Output inside myapp.log:

DEBUG - Debug message from custom logger
ERROR - Error occurred

8. Logging Exceptions

When errors occur, logging can capture them with full stack traces.
This is better than printing exceptions manually.

import logging

logging.basicConfig(level=logging.ERROR)

try:
   x = 10 / 0
except Exception as e:
   logging.exception("Exception occurred")

Output:

ERROR:root:Exception occurred
Traceback (most recent call last):
 File "<script>", line 5, in <module>
ZeroDivisionError: division by zero

9. Using Multiple Handlers

A common scenario is logging to both console and a file.

import logging

logger = logging.getLogger("multi")
logger.setLevel(logging.DEBUG)

file_handler = logging.FileHandler("multi.log")
console_handler = logging.StreamHandler()

formatter = logging.Formatter("%(levelname)s - %(message)s")

file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

logger.addHandler(file_handler)
logger.addHandler(console_handler)

logger.info("Logged in two places")

Output in Console:

INFO - Logged in two places

Output in multi.log:

INFO - Logged in two places

10. Rotating Log Files

Large applications generate large logs.
logging.handlers module supports rotating logs automatically.

Example: Rotating File Handler

import logging
from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler("rotate.log", maxBytes=2000, backupCount=5)

logging.basicConfig(
   handlers=[handler],
   level=logging.INFO,
   format="%(asctime)s - %(message)s"
)

for i in range(1000):
   logging.info("Logging line number %d", i)

This creates multiple logs when file size exceeds 2000 bytes.