Update a property in singleton (python)

Viewed 971

EDIT: Added Ralf's recommendation to use os.environ

Trying to implement a singleton logger so that everytime a logger instance is generated, it'll only write to a specific filename. Until a new filename is specified.

import logging

def singleton(myClass):                                             
    instances = {}                                                  
    def getInstance(*args,**kwargs):
        if myClass not in instances:
           instances[myClass] = myClass(*args,**kwargs)         
        return instances[myClass]                                   
    return getInstance                                              

@singleton
class Logger(object):
    def __init__(self):
        self.filename = os.environ['logger_filename']
        self.log = self.init_logger()

    def init_logger(self):
        fh = logging.FileHandler(self.filename + '.log')
        fmt = logging.Formatter("%(asctime)-6s: %(name)s - %(levelname)s - %(message)s)")
        fh.setFormatter(fmt)
        local_logger = logging.getLogger(self.filename)
        local_logger.setLevel(logging.DEBUG)
        local_logger.addHandler(fh)

        return local_logger

What i have above will always write in "test1.log" file.

os.environ['logger_filename'] = "test1"

a = Logger()
b = Logger()

a.log.debug("Hello A")
b.log.debug("Hello B")

os.environ['logger_filename'] = "test2"

c = Logger()
c.log.debug("Hello C")

Since the singleton will contain the Logger class in the instances, thus it will just return that instance and not run the __init__

If I update the singleton with the following:

def singleton(myClass):
    instances = {}
    def getInstance(*args,**kwargs):
        if myClass not in instances:
           instances[myClass] = myClass(*args,**kwargs)
        else:
            instances[myClass].update_logger()
        return instances[myClass]
    return getInstance

@singleton
class Logger(object):
    def __init__(self):
        self.filename = os.environ['logger_filename']
        self.log = self.init_logger()

    def init_logger(self):
        fh = logging.FileHandler(self.filename + '.log')
        fmt = logging.Formatter("%(asctime)-6s: %(name)s - %(levelname)s - %(message)s)")
        fh.setFormatter(fmt)
        local_logger = logging.getLogger(self.filename)
        local_logger.setLevel(logging.DEBUG)
        local_logger.addHandler(fh)

        return local_logger

    def update_logger(self):
        self.filename = os.environ['logger_filename']
        self.log = self.init_logger()


os.environ['logger_filename'] = "test1"


a = Logger()
b = Logger()

a.log.debug("Hello A")
b.log.debug("Hello B")

os.environ['logger_filename'] = "test2"

c = Logger()
c.log.debug("Hello C")

This will now generate 2 log files (test1 and test2). But test1 will contain two "Hello A" and two "Hello B". How do i get to fix that?

1 Answers

You give very few specific details, so I'll give an answer that should work, but I don't know if that is what you wanted.

How about you store the current filename in a environment variable using os.environ?

You can modify the singleton function:

import os

def singleton(myClass):                                             
    ...
        if myClass not in instances:
           kwargs['filename'] = os.environ['logger_filename']  # may raise KeyError
           instances[myClass] = myClass(*args, **kwargs)         
    ...

@singleton
class Logger(object):
    def __init__(self, filename):
        self.filename = filename
    ...

Or another option is to just modify the logger class:

import os

@singleton
class Logger(object):
    def __init__(self):
        self.filename = os.environ['logger_filename']  # may raise KeyError

With any of these two options you just have to modify the value of the environment variable to set a different target for your loggers:

import os
os.environ['logger_filename'] = 'f1.log'

b = Logger() 
b.log.debug("hello there")    # writes on 'f1.log'

os.environ['logger_filename'] = 'f2222.log'

c = Logger() 
c.log.debug("hello there")    # writes on 'f2222.log'
Related