pythonclasslogging

Python logging object is being accumulating on each instantiation


Consider the following code, where I have created a custom logger class and then in a for loop I am creating logger object multiple times, but instead of adding one log at a time it is adding multiple logs as shown below

import sys
import logging
import os

import configs

class Logger:
    def __init__(self, logger_name='logger', log_file_name='logs.log'):
        logger = logging.getLogger(logger_name)
        logger.setLevel(logging.DEBUG)

        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

        file_handler = logging.FileHandler(os.path.join(configs.LOGS_DIR, log_file_name))
        file_handler.setLevel(logging.INFO)
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)

        self.logger = logger
    
    def get_logger(self):
        return self.logger

for i in range(5):
    logger = Logger(logger_name=f'test', log_file_name='test').get_logger()
    logger.info(i)

I was expecting this:

2024-12-20 21:17:09,623 - test - INFO - 0
2024-12-20 21:17:09,623 - test - INFO - 1
2024-12-20 21:17:09,623 - test - INFO - 2
2024-12-20 21:17:09,623 - test - INFO - 3
2024-12-20 21:17:09,642 - test - INFO - 4

but this is the output:

2024-12-20 21:17:09,623 - test - INFO - 0
2024-12-20 21:17:09,623 - test - INFO - 1
2024-12-20 21:17:09,623 - test - INFO - 1
2024-12-20 21:17:09,623 - test - INFO - 2
2024-12-20 21:17:09,623 - test - INFO - 2
2024-12-20 21:17:09,623 - test - INFO - 2
2024-12-20 21:17:09,623 - test - INFO - 3
2024-12-20 21:17:09,623 - test - INFO - 3
2024-12-20 21:17:09,623 - test - INFO - 3
2024-12-20 21:17:09,623 - test - INFO - 3
2024-12-20 21:17:09,642 - test - INFO - 4
2024-12-20 21:17:09,642 - test - INFO - 4
2024-12-20 21:17:09,642 - test - INFO - 4
2024-12-20 21:17:09,642 - test - INFO - 4
2024-12-20 21:17:09,642 - test - INFO - 4

Why this is happening and how to fix this?


Solution

  • I solved this issue by creating Logger class like this:

    class Logger:
        @staticmethod
        def __create_file_handler__(log_file_path, level, formatter):
            file_handler = logging.FileHandler(log_file_path)
            file_handler.setLevel(level)
            file_handler.setFormatter(formatter)
            return file_handler
        
        @staticmethod
        def get_logger(logger_name='logger', log_file_name='logs.log'):
            logger = logging.getLogger(logger_name)
            
            formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
            file_handler_file_path = os.path.join(configs.LOGS_DIR, log_file_name)
            file_handler_level = logging.INFO
            
            if logger.hasHandlers():
                file_handler = [_ for _ in logger.handlers if isinstance(_, logging.FileHandler)]
                
                if file_handler:
                    file_handler = file_handler[0]
                    if file_handler.baseFilename != log_file_name:
                        logger.removeHandler(file_handler)
                        logger.addHandler(Logger.__create_file_handler__(
                            file_handler_file_path,
                            file_handler_level,
                            formatter
                        ))
                
                return logger
            
            logger.setLevel(logging.DEBUG)
            logger.addHandler(Logger.__create_file_handler__(
                file_handler_file_path,
                file_handler_level,
                formatter
            ))
    
            return logger
    

    And then use logger in the for loop like this

    for i in range(5):
        logger = Logger.get_logger(logger_name=f'test', log_file_name='test')
        logger.info(i)
    

    This solves the problem.

    Thank you @john-gordon [John Gordon] For giving me a insight to think about it in another way.