weegolo
weegolo

Reputation: 396

Duplicate messages from a class logger

My class constructs a logger named after the class in init(). Now when I log a message, I get one copy of the message for every instance of the class that has been created so far. How do I avoid this duplication?

I'm also concerned that this might just be a symptom of a deeper problem - is this a wasteful approach to memory management in an application that may instantiate a few hundred classes at a time? Is there a more sensible way of handling logging across multiple classes?

While the code below is a simplistic recreation of the problem, the actual code is very modular and each class may be used in many different ways by a variety of different applications. Each class might be used as a standalone class invoked by the main() method defined with it, or it might be invoked from another class, or from one of three or four main applications. The approach to logging needs to allow for this modularity.

import logging

class person:
    def __init__(self,name):
        self.name = name
        logger = logging.getLogger('person logger')
        logger.setLevel(logging.DEBUG)
        ch = logging.StreamHandler()
        formatter = logging.Formatter('%(name)s - %(message)s')
        ch.setFormatter(formatter)
        logger.addHandler(ch)

        logger.info(f'{self.name} exists')


if __name__ == '__main__':

    for name in ["Alice", "Bob", "Carlos"] :
        a = person(name)

Expected results:

person logger - Alice exists
person logger - Bob exists
person logger - Carlos exists

Actual results:

person logger - Alice exists
person logger - Bob exists
person logger - Bob exists
person logger - Carlos exists
person logger - Carlos exists
person logger - Carlos exists

Upvotes: 3

Views: 290

Answers (1)

Sraw
Sraw

Reputation: 20224

Use if logger.handlers to check if there have already been handlers. Or you are adding handler every time you create that person object.

Basically:

import logging

class person:
    def __init__(self,name):
        self.name = name
        logger = logging.getLogger('person logger')
        logger.setLevel(logging.DEBUG)
        if not logger.handlers:
            ch = logging.StreamHandler()
            formatter = logging.Formatter('%(name)s - %(message)s')
            ch.setFormatter(formatter)
            logger.addHandler(ch)

        logger.info(f'{self.name} exists')


if __name__ == '__main__':

    for name in ["Alice", "Bob", "Carlos"] :
        a = person(name)

Further, mostly we don't create a logger for a class but for a module.

For a huge project, create a log_helper module which may contain initialization function:

def getLogger(name):
    logger = logging.getLogger(name)
    # do some initialization...
    # like adding handlers
    return logger

And get logger at the top of each module:

# person.py  Module person

import log_helper

logger = log_helper.getLogger("person") # or whatever you want

# just use this logger...

Upvotes: 4

Related Questions