Customized Logging in Python

Customized Logging in Python

In this article, I am going to discuss Customized Logging in Python with examples. Please read our previous article where we discussed Logging Module in Python. At the end of this article, you will understand the following pointers in detail.

  1. Root Logger in Python Logging Module
  2. Problems with root logger
  3. Customized Logging in Python
  4. Steps for creating a custom logger
Root Logger in Python Logging Module

In all the above examples, the logger we have initialized is root logger. When we use basicConfig() method, then, by default we are initializing the root logger.

In general, it is advised not to use the root logger. Instead, we should define and customize our own logger for each module or package. The examples shown above are only for basic understanding of what a logger is.

Problems with root logger:
  1. Once we set basic configuration then that configuration is final, and we cannot change it in the course of the program.
  2. It will always work for only one handler at a time, either console or file, but not both simultaneously.
  3. It is not possible to configure a logger with different configurations at different levels.

To overcome these problems, we should go for our own customized loggers.

Customized Logging in Python:

In python, the logging module has mainly four components: loggers, handlers, filters and formatters. Loggers are the objects of the logging module with which we handle the logging functionality. Handlers are those which specify where the logging should go to i.e console or files etc. Filters are those with which we can tell the logger what logs to record i.e levels of logging. Formatters are those which are used to customize the log messages.

Steps for creating a custom logger

Step1: Create a logger

First we need to create a logger, which is nothing but an object to the logger class. We can create this by using getLogger() method. After creating the logger object we have to set the log level using setLevel() method.

logger = logging.getLogger(‘demologger’)
logger.setLevel(logging.INFO)

We can give any name to the logger as we wish. In the above example, a logger with name ‘demologger’ will be created.

Step2: Creating handler

The next step is to create a handler object and set the logging level. There are several types of Handlers like StreamHandler, FileHandler etc. If we use StreamHandler then log messages will be printed to the console. If we use FileHandler, then the log messages will be printed into file.

For Stream Handler,
consoleHandler = logging.StreamHandler()
consoleHandler.setLevel(logging.INFO)

For File Handler,
fileHandler = logging.FileHandler(‘test.log’)
fileHandler.setLevel(logging.INFO)

Step3: Creating Formatter

The next step is to create a formatter object.

formatter = logging.Formatter(‘%(asctime)s – %(name)s – %(levelname)s:
%(message)s’, datefmt=’%d/%m/%Y %I:%M:%S %p’)

Step4: Adding Formatter to Handler

Now we have to add the Formatter (object) to Handler (object) using setFormatter() method.

consoleHandler.setFormatter(formatter)

Step5: Adding Handler object to the Logger

Then the handler object should be added to the logger object using addHandler() method.

logger.addHandler(fileHandler)
logger.addHandler(streamHandler)

Step6: Writing the log messages

The last step is writing the log messages to the file using the methods and logger object which we created.

  1. logger.debug(‘debug message’)
  2. logger.info(‘info message’)
  3. logger.warn(‘warn message’)
  4. logger.error(‘error message’)
  5. logger.critical(‘critical message’)
Points to note:
  1. We have set the logging level for the logger object and the handler object as well.
  2. If we haven’t set any level for the logger object then, by default, the logger object will be set to WARNING level.
  3. If we haven’t set any level for the handler object then, by default, the logger object’s level will be considered for the handler object also.
  4. If we are not satisfied with logger level, then we can set log level explicitly at handler level.
  5. If we have set different log levels for the logger and handler then, the handler level should be supported by the logger. i.e logger log level should be lower than handler level. If this is not satisfied, the only logger log level will be considered.

For example,
logger → DEBUG
handler → INFO ⇒ Valid and INFO level will be considered for the handler
logger → INFO
handler → DEBUG ⇒ Invalid and INFO level will be considered for the handler

Program: Customized Logging in Python (demo9.py)
import logging
class LoggerDemoConsole:
   def testLog(self):
       logger = logging.getLogger('demologger')
       logger.setLevel(logging.INFO)
       consoleHandler = logging.StreamHandler()
       consoleHandler.setLevel(logging.INFO)
       formatter = logging.Formatter('%(asctime)s - %(name)s%(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S%p')
       consoleHandler.setFormatter(formatter)
       logger.addHandler(consoleHandler)
       logger.debug('debug message')
       logger.info('info message')
       logger.warn('warn message')
       logger.error('error message')
       logger.critical('critical message')
demo = LoggerDemoConsole()
demo.testLog()

Output:

Customized Logging in Python

If we want to use class name as logger name, then we have to create logger object as follows

Program: class name as logger name in Python (demo10.py)
import logging
class LoggerDemoConsole:
   def testLog(self):
       #logger = logging.getLogger('demologger')
       logger = logging.getLogger(LoggerDemoConsole.__name__)
       logger.setLevel(logging.INFO)
       consoleHandler = logging.StreamHandler()
       consoleHandler.setLevel(logging.INFO)
       formatter = logging.Formatter('%(asctime)s - %(name)s%(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S%p')
       consoleHandler.setFormatter(formatter)
       logger.addHandler(consoleHandler)
       logger.debug('debug message')
       logger.info('info message')
       logger.warn('warn message')
       logger.error('error message')
       logger.critical('critical message')
demo = LoggerDemoConsole()
demo.testLog()

Output:

class name as logger name in Python

Program: using file handler in Python (demo11.py)
import logging
class LoggerDemoConsole:
   def testLog(self):
       #logger = logging.getLogger('demologger')
       logger = logging.getLogger(LoggerDemoConsole.__name__)
       logger.setLevel(logging.INFO)
       fileHandler = logging.FileHandler("demo.log",mode='a')
       fileHandler.setLevel(logging.INFO)
       formatter = logging.Formatter('%(asctime)s - %(name)s%(levelname)s: %(message)s', datefmt='%m/%d/%Y %I:%M:%S%p')
       fileHandler.setFormatter(formatter)
       logger.addHandler(fileHandler)
       logger.debug('debug message')
       logger.info('info message')
       logger.warn('warn message')
       logger.error('error message')
       logger.critical('critical message')
demo = LoggerDemoConsole()
demo.testLog()
print("Done")

Output: using file handler in Python

File demo.log

Customized Logging in Python with Examples

In the next article, I am going to discuss Multithreading in Python. Here, in this article, I try to explain Customized Logging in Python with Examples. I hope you enjoy this Customized Logging in Python with Examples article. I would like to have your feedback. Please post your feedback, question, or comments about this article.

Leave a Reply

Your email address will not be published.