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?
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