Print Logs in Python

Logging is used to track events when some software runs. An event is a descriptive message which can optionally contain variable data.

Logging Functions

Logging provides a set of easy to use functions for simple logging usage in Python. These functions are debug(), info(), warning(), error(), and critical(). These functions are named after the level of severity of the events that they are used for tracking. The usage of these functions are described below:

Level Usage
DEBUG To track detailed information when diagnosing problems.
INFO To confirm things are working as expected.
WARNING To indicate that everything is working as expected but something unexpected happened and may cause problems in the future. For Example - Low disk space.
ERROR To indicate some serious problem occured due to which some functions were failed to run.
CRITICAL To indicate a serious problem due to which the program itself may not continue to work.

A Simple Logging

A simple logging example:


import logging;

logging.warning("File size too big")
logging.info("Started to run!")

If you add the above lines to your code and run it, you will see:

WARNING:root:File size too big

The INFO message is not printed because the default level is WARNING. The logging message includes the severity level of the log and the description of the event.

Logging to a File

For most applications, log events are usually written to a file, lets look at it next:


import logging;

logging.basicConfig(filename='example.log', level=logging.DEBUG)
logging.warning("File size too big")
logging.info("Started to run!")
logging.error("Something went wrong!")
logging.debug("Debugging a problem")
logging.critical("Something serious is causing the application to stop!")

The call to basicConfig should be made before any calls to info(), debug(), error(), etc.

If the above script is run several times, the log messages from every successive runs are appended to the file example.log. We can also discard the messages from earlier runs by specifying the filemode argument as shown in the example below:


logging.basicConfig(filename='example.log', filemode='w', level=logging.DEBUG)

Logging Variable Data

Use a format string for the event log message and append the variable data as arguments.

Example

import logging;

logging.warning("%s file size is % which is too big!", 'text.')

If you run the above script, you'll see:

WARNING:root:text.log file size is 200MB which is too big!

Change the Format of Log Messages

Use the format argument to change the format of displaying log messages.

Example

import logging;

logging.basicConfig(format='%(levelname)s: %(message)s')
logging.warning("Changing format of log messages")

If you run the above script, you'll see:

WARNING: Changing format of log messages

Show Datetime in Log Messages

Use the %(asctime)s in format argument to display datetime of an event in log messages. You can also use datefmt argument to format datetime.

Example

import logging

logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S %p')
logging.warning("Displaying date in log messages")

If you run the above script, you'll see:

04/15/2021 10:46:15 AM: WARNING: Displaying date in log messages

Advanced Logging

The logging library takes a modular approach and provides the components as follows:

  • Loggers — class whose objects helps to call the logging functions directly in the application code.
  • Handlers — sends log records created by loggers to the appropriate destination.
  • Filters — helps to filter log records.
  • Formatters — helps to format the log messages.

Rotating disk log files in Python

To rotate disk log files in Python, you can use RotatingFileHandler class, located in the logging.handlers module.

The use of maxBytes and backupCount values in RotatingFileHandler allows the file to rollover at a predetermined size. Rollover never occurs if the value of maxBytes and backupCount is zero. When the current log file size is nearly the maxBytes value in length, the rollover occurs. The value of backupCount argument determines the number of files, the system will create to save old log files by appending the extensions .1, .2, .3, etc., to the filename. For example, with a backupCount of 6 and a base file name of example.log, you would get example.log, example.log.1, example.log.2, example.log.3, example.log.4, example.log.5, and example.log.6. The log messages being written to is always example.log file. When the file size is nearly the maxBytes, it is closed and renamed to example.log.1 and if there is example.log.1 and example.log.2, then they are renamed to example.log.3 and example.log.4.

Here is an example of rotating disk log files in Python:

Example
my_logger.py

import logging
from logging.handlers import RotatingFileHandler
import os
import sys
from pathlib import Path
import datetime


def Logger(log_filename):
    if not log_filename:
        log_filename = str(Path(__file__).parent.parent) + "/logs/default.log"
    try:
        # Create logger
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.INFO)

        # Create handlers
        str_handler = logging.StreamHandler()
        file_handler = logging.handlers.RotatingFileHandler(
            log_filename, mode='a', maxBytes=100000000, backupCount=4)
        str_handler.setLevel(logging.INFO)
        file_handler.setLevel(logging.INFO)

        # Create formatters and add it to handlers
        str_format = logging.Formatter(
            '%(asctime)s - %(filename)s - %(levelname)s - %(message)s')
        file_format = logging.Formatter(
            '%(asctime)s - %(filename)s - %(levelname)s - %(message)s')
        str_handler.setFormatter(str_format)
        file_handler.setFormatter(file_format)

        # Add handlers to the logger
        logger.addHandler(str_handler)
        logger.addHandler(file_handler)
        return logger
    except Exception:
        err = sys.exc_info()
        print("Error : %s" % (err))
test.py

from my_logger import Logger


def my_first_function(logger):
    logger.info("Running first function")
    logger.error("Error in first function")


def my_second_function(logger):
    logger.info("Running second function")
    logger.error("Error in second function")


logger = Logger("example.log")
my_first_function(logger)
my_second_function(logger)

If you run test.py file, you should see this in example.log:

2021-04-15 13:02:01,148 - tezt.py - INFO - Running first function
2021-04-15 13:02:01,148 - tezt.py - ERROR - Error in first function
2021-04-15 13:02:01,148 - tezt.py - INFO - Running second function
2021-04-15 13:02:01,148 - tezt.py - ERROR - Error inn second function