0

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
  • This function already behaves the way you expect. It will always return the same logger object, because `logging.getLogger` takes care of that. – zvone Oct 19 '20 at 00:45
  • @zvone No it does not. Each time I call this function it creates a different instance. I am calling the module, that is containing this function, from different modules. So each time its called it creates different instance of object. But I want same instance throughout – Ridwan Chowdhury Oct 19 '20 at 00:56
  • `__name__` is the name of the current module which is why a different log is created for each module. You can use any string for the name. Try `logger = logging.getLogger('MyLog')` for every instance call so it uses the same log each time. – Mike67 Oct 19 '20 at 02:32
  • @Mike67 I just tried it by substituting __name__ with 'MyLog' and it did not work. I explicitly printed out the objects that were created and they were all different for different modules – Ridwan Chowdhury Oct 19 '20 at 02:54

1 Answers1

0

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
Mike67
  • 11,175
  • 2
  • 7
  • 15
  • This logic makes sense but you have to understand that this setLogger function is itself in a module called lets say debugLogs.py. So, suppose I try to setup my logger in a file called A.py I import debugLogs.py and setup logger. Then another module B.py tries to setup logger and calls debugLogs.py but even for B.py the logger would be initially set to None so again we are getting different objects for different files. How do you reckon the logic should be such that only when we call from A.py logger is setup and we can reuse that logger object created by A.py through other modules. – Ridwan Chowdhury Oct 19 '20 at 17:15