Search code examples
pythonclassloggingmixinssuperclass

In the file where I define a mixin class, how do I use the logger from the file where the mixin is used?


I'm abstracting away a repetitive chunk of code where I set up a logger in many classes.

I'd like to have a class where a mixin allows me to either instantiate the class with a logger object, or use the default module in that class' module.

I'd use it like this:

import logging
from .mixin import LoggerMixin


resource_module_logger = logging.getLogger(__name__)


class MyResource(LoggerMixin):
    """ Instantiate this class to do something.

    If instantiated with a custom logger, then all log outputs will go to that. Otherwise, they'll go to resource_module_logger

    ```
    MyResource().do_something()  # Does something while logging to resource module logger.
    MyResource(logger=logging.getLogger("specific"))  # does something while logging to a specific logger.
    ```
    """

    def do_something(self):
        self.logger.info("This message will go to the default module level logger, unless a custom logger was specified at instantiation")

The mixin I have so far is:

import logging


this_is_not_the_logger_im_looking_for = logging.getLogger(__name__)


class LoggerMixin:

    def __init__(self, *args, logger=None, **kwargs):

        self._logger = logger
        super().__init__(*args, **kwargs)

    @property
    def logger(self):
        # How do I get the resource module logger for the MyResource class which this is mixed into???
        resource_module_logger = ?????????????
        return self._logger or resource_module_logger

The Question Is it possible to get that logger from within the mixin, and therefore abstract this away fully (and if so, how?) or do I have to override the logger property for every class?


Solution

  • Rather than instantiating a global in the module and then trying to access it, you could just use getLogger directly to fetch the logger based on the module name. Also, I'd just do the defaulting once in __init__ instead of on every lookup in the property:

    self._logger = logger or logging.getLogger(self.__class__.__module__)
    

    Edit -- include complete solution

    The complete mixin, loggable.py, would then be:

    import logging
    
    
    class Loggable:
        """ Mixin to allow instantiation of a class with a logger, or by default use the module logger from that class
    
        ```
        class MyResource(Logged):
            def do_something(self):
                self.logger.info('write to a logger')
    
        MyResource().do_something()  # Log statements go to the default logger for the module in which MyResource is a member
        MyResource(logger=logging.getLogger("specific"))  # Log statements go to the specific logger.
        ```
        """
    
        def __init__(self, *args, logger=None, **kwargs):
            super().__init__(*args, **kwargs)
            self._logger = logger or logging.getLogger(self.__class__.__module__)
    
        @property
        def logger(self):
            return self._logger