Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logging from an abstract class

I have three Python modules: one contains an (abstract) parent class, and two contain classes implementing this parent class. Parent class has some concrete and some abstract methods, it looks like this:

moduleA:

from abc import ABC, abstractmethod
import logging
class Parent(ABC):
    def __init__(self):
        logger.info("Hello world from base class!")

    def concreteMethod(self, something):
        return something

    @abstractmethod
    def additionalMethod(self, something):
        """it needs to be implemented"""

moduleB:

import logging
from moduleA import Parent
logger = logging.getLogger("b_logger")
fh = logging.FileHandler('b_logger.log')
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)

class ChildB(Parent):
    def __init__(self):
        super().__init__()

    def additionalMethod(self):
        logger.info("Hello world from B")

moduleC:

import logging
from moduleA import Parent
logger = logging.getLogger("c_logger")
fh = logging.FileHandler('c_logger.log')
fh.setLevel(logging.DEBUG)
logger.addHandler(fh)

class ChildC(Parent):
    def __init__(self):     
        super().__init__()      

    def additionalMethod(self):
        logger.info("Hello world from C")

Now if I decide to run:

from moduleB import ChildB
from moduleC import ChildC

B = ChildB()
B.additionalMethod()
C = ChildC()
C.additionalMethod()

I want to have two log files, each with one line from the parent class, and one from the child class.

My problem is, how do I configure the parent class logger to log into the child class' log file (not to its own)?

like image 886
machaerus Avatar asked Apr 20 '26 14:04

machaerus


1 Answers

If you want a subclass to determine the logger, then you'd have to make the logger an attribute of the subclasses. The ABC could document this requirement with an abstract property:

class Parent(ABC):
    def __init__(self):
        self.logger.info("Hello world from base class!")

    @property
    @abstractmethod
    def logger(self):
        """A logger object (can be a class attribute) to log messages to."""

    def concreteMethod(self, something):
        return something

    @abstractmethod
    def additionalMethod(self, something):
        """it needs to be implemented"""

then set the logger attribute on your child classes.

If you need the logger to be a module global, then the name logger can make it difficult to reuse the same object as a class attribute. You can work around this by using a different name for the class attribute (so not Parent.logger but something else), or by adding an alias for the logger in the module:

logger = b_logger = logging.getLogger("b_logger")

class ChildB(Parent):
    logger = b_logger

    def additionalMethod(self):
        self.logger.info("Hello world from B")

The additionalMethod() method above uses the class attribute, but you are of course free to use the global names logger or b_logger too. Using self.logger would allow for further subclasses to change the logger reference again.

like image 73
Martijn Pieters Avatar answered Apr 22 '26 04:04

Martijn Pieters