Logging as the caller module in Python

2019-03-02 10:55发布

I have the following application structure:

./utils.py
def do_something(logger=None):
    if not logger:
        logger = logging.getLogger(__name__)

    print('hello')
    logger.debug('test')

./one.py
from utils import do_something
logger = logging.getLogger(__name__)
do_something(logger=logger)

./two.py
from utils import do_something
logger = logging.getLogger(__name__)
do_something(logger=logger)

Now, then this runs, the logging output will show the names of the respective modules that are using the functions (one and two) rather than utils. I then use this information (the logger's name) to filter the messages.

Is there a way to do this without having to pass the logger though as an argument? Would I have to basically introspect the caller function and initialize a logger based on that? What's the common solution for this type of recipe, when one function is used all over the code base but the loggers should be of those calling the function?

3条回答
Lonely孤独者°
2楼-- · 2019-03-02 11:29

Passing the logger as an argument looks fine to me. But if you have many calls to do_something then passing the same logger in each call is not only too verbose but prone to errors --you might pass the wrong logger by mistake. Also, while you do need to make do_something aware of the logger, doing it at the point where you are calling it probably does not make sense --you want to do something, not mess with loggers. OOP to the rescue: make do_something a method of a class and pass the logger when instantiating the class, then you do not need to pass it to the method.

# utils.py
class UsefulObject:
    def __init__(self, logger):
        self._logger = logger

    def do_something(self):
        print('hello')
        self._logger.debug('test')

Now in the client module you create an instance of UsefulObject and you can call .do_something on it without passing the logger every time.

# one.py
from utils import UsefulObject
logger = logging.getLogger(__name__)
useful_object = UsefulObject(logger)  # do this once
# ...
useful_object.do_something()  # do this many times, do not pass the logger
查看更多
爷、活的狠高调
3楼-- · 2019-03-02 11:35

Variable logger being a global, can be accessed from inside do_something() function like this:

logger = logging.getLogger(__name__)
def do_something():
    x = logger

After reading this carefully:

What's the common solution for this type of recipe, when one function is used all over the code base but the loggers should be of those calling the function?

In simple english would be:

How to access global variable logger from imported function do_something()?

I conclude that there is no other way!
You have to pass logger as an argument for this particular case.

from utils import do_something
logger = logging.getLogger(__name__)

do_something()  # logger global is not visible in do_something()
do_something(logger=logger)  # the only way
查看更多
放荡不羁爱自由
4楼-- · 2019-03-02 11:43

If you don't like the existing solutions, here's a magic way to do it:

def do_something():
    logger = inspect.currentframe().f_back.f_globals['log']
    ...

The calling function must then have a global variable called log.

查看更多
登录 后发表回答