简体   繁体   中英

Monkeypatching logging.Logger — how do arguments work?

I have a logging class that I've written that I like to use. It looks like this:

class EasyLogger(object):
    SEP = " "

    def __init__(self, logger=logging.getLogger(__name__)):
        self.logger = logger

    def _format_str(self, *args):
        return self.SEP.join([str(a) for a in args])

    def debug(self, *args):
        self.logger.debug(self._format_str(*args))

    .... repeated for the other logging methods, like info, warning, etc....

    def __getattr__(self, name):
        return getattr(self.logger, name)

This gives me syntax like:

LOG.debug("some_variable: ", some_variable)

which I like. I don't want to inherit from logging.Logger because composition is better than inheritance and I don't really want to mess around with builtin classes like that.

My format string looks like:

LOGGING_FMT = "<%(filename)s:%(lineno)s(%(levelname)s) - %(funcName)s() >"\
                        "%(message)s"

but this doesn't work, because the line numbers and function name always report as something inside easylogger.py rather than the calling function.

I've fixed this issue by following this fantastic answer and monkeypatching the findCaller method of EasyLogger.logger , which looks like this:

def __init__(self, logger=logging.getLogger(__name__)):
    self.logger = logger
    # Ugly, ugly, ugly dirty hack to fix line numbers
    self.logger.findCaller = find_caller_monkeypatch

This works, but I'm confused. I originally defined find_caller_monkeypatch with the following signature:

def find_caller_monkeypatch(self):

(the body of the function is copy+pasted from the answer that's linked). However, this causes an error:

  File "/usr/lib64/python2.7/logging/__init__.py", line 1262, in _log
    fn, lno, func = self.findCaller()
TypeError: find_caller_monkeypatch() takes exactly 1 argument (0 given)

Removing self as an argument to find_caller_multipatch fixes the error, but I'm confused: why isn't find_caller_monkeypatch getting self as an argument when it's called as self.findCaller() ? Do methods only get self as a parameter if they're defined inside of the class?

The following snippet is a smaller example of the same issue:

In [7]: class Foo(object):
   ...:     def say_hi(self):
   ...:         print "Hello World"
   ...:         

In [8]: def say_bye_self(self): print "Goodbye world"

In [9]: def say_bye(): print "Goodbye world"

In [10]: foo = Foo()

In [11]: foo.say_hi()
Hello World

In [12]: foo.say_hi = say_bye_self

In [13]: foo.say_hi()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-10baea358e0b> in <module>()
----> 1 foo.say_hi()

TypeError: say_bye_self() takes exactly 1 argument (0 given)

In [14]: foo.say_hi = say_bye

In [15]: foo.say_hi()
Goodbye world

What's going on here?

You can Monkeypatch on the class

Foo.say_hi = say_bye_self
foo = Foo()
foo.say_hi()

Or you can Monkeypatch an instance

import types
foo.say_hi = types.MethodType(say_bye_self, foo)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM