user1403505
user1403505

Reputation: 1005

Maximum recursion depth exceeded when using singleton logging adapter in Django

I am implementing logging for a Django web application through the Logging adapter. I am sending a custom prefix which is "[email protected]" before each logging message to identify the user logged in.

I have made the loggingAdapter as singleton as when I logout of the application it just removes the session but the program execution will remain, and the next time I login the user prefix should not be appended to the existing prefix, due to the loggingAdapter object still not getting destroyed.

logger=logging.getLogger("billingacct") #This is outside of any class and I am getting the "billingacct" value from the settings.py

    class LoggerAdapter(logging.LoggerAdapter):
      _instance=None
      def __new__(cls,logger,prefix):
        if cls._instance is None:
          print('Creating the object')
          cls._instance = super(LoggerAdapter, cls).__new__(cls)

          cls.prefix=prefix
                # Put any initialization here.
          return cls._instance
        else:
          print('Object already exists')
          cls.prefix=prefix
          return cls._instance


    def process(self, msg, kwargs):
      return '[%s] %s' % (self.prefix, msg), kwargs

I call the above class in a entry class:

def login:
    global logger
    email=<value set>
    logger = LoggerAdapter(logger, email)

The logging works fine first time, if I logout and login immediately I get this error:

**Object already exists** #this is from the console, below from django logs

[19/Mar/2021 10:59:16] ERROR [django.request:224] Internal Server Error: /login
Traceback (most recent call last):
  File 
billingacct/views.py", line 308, in login
logger.info("{} ".format(request.session['role']))


File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1794, in info
    self.log(INFO, msg, *args, **kwargs)



File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1830, in log
    if self.isEnabledFor(level):
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1838, in isEnabledFor
    return self.logger.isEnabledFor(level)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1838, in isEnabledFor
    return self.logger.isEnabledFor(level)
  File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/logging/__init__.py", line 1838, in isEnabledFor
    return self.logger.isEnabledFor(level)
  [Previous line repeated 958 more times]
RecursionError: maximum recursion depth exceeded

How can I solve this?

Upvotes: 0

Views: 771

Answers (1)

AaronH
AaronH

Reputation: 409

Thanks for posting your question! I had a similar issue, not related to django. Was a real head scratcher because I was only seeing the RecursionError under heavy load. In my case the AWS Lambda function container was usually recycling before I hit the recursion limit, but under heavy load...

What's happening is that you are instantiating your logger as a global

logger=logging.getLogger("billingacct")

And then when your function is first called, the global is being set to a log adapter object, with the logger inside it.

def login:
    global logger
    email=<value set>
    logger = LoggerAdapter(logger, email)

In subsequent calls the adapter is being nested inside more adapters until you hit the recursion depth limit.

You want to check that the type of the global before you do this, if it's a logger, put it inside the adapter, if it's an adaptor, get its logger object and put it inside a new adapter.

def login:
    global logger
    email=<value set>
    if isinstance(logger, logging.Logger):
        logger = LoggerAdapter(logger, email)
    elif isinstance(logger, LoggerAdapter):
        logger = LoggerAdapter(logger.logger, email)
    

Upvotes: 1

Related Questions