Search code examples
pythonpython-2.7loggingsingletonsingle-instance

Converting a function into a singleton class that returns one instance


I am doing this project on Python 2.7 and I am trying to convert this function that returns a logging object. I am trying to make sure that I can utilize the same object instance through different python modules by importing it without actually creating new instances? For e.g if I actually import the module where the instance was created originally, each time I import it a new instance gets created. But I want to use the same original instance, throughout all different modules, that was created the first time I ran that module since multiple lines are being printed out inside my log file due to multiple instances. That is why a singleton class in Python 2.7 is required but I am not sure how to convert this function into a singleton class such that it returns one instance and I can use it throughout all my different modules by importing without triggering new instances. The setLogger function creates a logger instance which inputs logs into the file_name log file.

def setLogger(file_name):
    logger = logging.getLogger(__name__)
    if not getattr(logger, 'handler_set', None):
        logger.setLevel(logging.INFO)
        stream_handler = logging.StreamHandler()
        file_handler = logging.FileHandler(file_name)
        formatter = logging.Formatter('%(message)s')
        file_handler.setFormatter(formatter)
        logger.addHandler(file_handler)
        logger.addHandler(stream_handler)
        logger.setLevel(logging.INFO)
        logger.propagate = False
        logger.handler_set = True
    return logger

Solution

  • I don't understand why different loggers are generated when using the same name.

    To force a single logger instance, get the logger once and store it in a global variable.

    logger = None  # global variable
    
    def setLogger(file_name):
        global logger
        if logger == None:
           print('Creating logger')
           logger = logging.getLogger(__name__)  # only run once
        if not getattr(logger, 'handler_set', None):
            ........
        return logger
    

    +++++++++++++++++++++++++++++++++++++++++++++++

    I set up some modules to narrow down the issue.

    --- mylogger.py ---

      import logging
    
      logger = None  # global variable
    
      def setLogger(file_name="LogOut.log"):
          global logger
          if logger == None:
             print('Creating logger')
             logger = logging.getLogger(__name__)  # only run once
          if not getattr(logger, 'handler_set', None):
              logger.setLevel(logging.INFO)
              stream_handler = logging.StreamHandler()
              file_handler = logging.FileHandler(file_name)
              formatter = logging.Formatter('%(message)s')
              file_handler.setFormatter(formatter)
              logger.addHandler(file_handler)
              logger.addHandler(stream_handler)
              logger.setLevel(logging.INFO)
              logger.propagate = False
              logger.handler_set = True
          return logger
    

    --- modc.py ---

      import mylogger
    
      def writelog():
          print("Hello from " + __name__)
          print(__name__, 'LogID:', id(mylogger.setLogger()))
          mylogger.setLogger().warning("Hello from " + __name__)
    

    --- modb.py ---

      import mylogger
      import modc
    
      def writelog():
          print("Hello from " + __name__)
          print(__name__, 'LogID:', id(mylogger.setLogger()))
          mylogger.setLogger().warning("Hello from " + __name__)
    
      modc.writelog()
    

    --- moda.py ---

      import mylogger
    
      def writelog():
          print("Hello from " + __name__)
          print(__name__, 'LogID:', id(mylogger.setLogger()))
          mylogger.setLogger().warning("Hello from " + __name__)
          
      import modb
      import modc
    
      writelog()
      modb.writelog()
      modc.writelog()
    

    I ran moda.py. Here is the output. The logger is created once and every module uses the same instance of the logger. All messages are saved to the same file. Note that I'm using python 3.7.

      Hello from modc
      Creating logger
      modc LogID: 1764613056456
      Hello from modc
      Hello from __main__
      __main__ LogID: 1764613056456
      Hello from __main__
      Hello from modb
      modb LogID: 1764613056456
      Hello from modb
      Hello from modc
      modc LogID: 1764613056456
      Hello from modc