Eric S. Johansson wrote:

> Peter Otten wrote:
>> Eric S. Johansson wrote:
>> 
>>> I need to to be able to conditionally log based on the method the log
>>> statement is in and one other factor like a log level.  in order to do
>>> so, I need to be able to automatically find out the name of the method
>>> and its class but I haven't found out how to do that yet.
>>>
>>> for example,
>>>
>>> class catus(Felis):
>>>    def Siamese_cat( yowl, purr, demand_food):
>>>
>>>      ...
>>>      log("state of litter box %s"% litter_box.smell, level = 1)
>>>
>>>
>>> If the table of methods logged contains "catus.Siamese_cat", then I
>>> would expect to see the output of the log statements in a log file.  If
>>> not then I wouldn't see anything in the log.
>>>
>>> Has somebody done this already?  Is it even possible to do without
>>> manually adding the class and method information for every log
>>> statement?
>>>
>>> a related question is using signals for reloading configurations etc.  I
>>> have to find any good examples of how to use signals to cause a
>>> long-running process to reload external data.  Any good pointers?
>> 
>> Instead of rolling your own, use the logging package which can handle
>> everything but the class info out of the box (levels are spelt as method
>> names info(), warn() etc.).
> 
> I was planning on using logging.  I've been using syslog for too long
>> 
>> import logging
>> 
>> class LoggedType(type):
>>     def __new__(mcl, name, bases, classdict):
>>         classdict["logger"] = logging.getLogger(name)
>>         return type.__new__(mcl, name, bases, classdict)
> 
> __new__ is new to me.  took a look at
> http://www.python.org/download/releases/2.2.3/descrintro/#__new__ which
> give me some clue but not enough.  what I get is that in call
> initialization, you add an entry to the class dict (adds new method??).
> I see that name, bases, or classdict are part of the normal class
> construction process and refer to the class under construction.  I'm
> guessing mcl comes from __metaclass__ and defaults to type?

The first parameter to __new__() is always the actual class (here
LoggedType). Because LoggedType's purpose is to serve as a metaclass I used
the shortcut mcl.

> the getLogger creates a logging channel so there is one channel per
> class?  but what selects the class for output or is that a derived
> logger class I need to create?

As a general direction try to understand the logging package, the __new__()
method (a constructor in C++), and metaclasses (a class is an instance of
another class which is called metaclass to avoid confusion when talking
about both) independently before jumping into the mix.

> also, how could one automatically determine the method doing the logging?
> 
>> 
>> class Felis:
>>     __metaclass__ = LoggedType
> 
> needed in every top level class?

Yes. The good news is that you only need one toplevel class

class Logged:
   __metaclass__ = LoggedType

that you can inherit from.

>>     def alpha(self):
>>         self.logger.info("felis-alpha")
>> 
>> class Catus(Felis):
>>     def alpha(self):
>>         self.logger.info("catus-alpha")
>>     def beta(self):
>>         self.logger.info("catus-beta")
>> 
>> if __name__ == "__main__":
>>     logging.basicConfig(format="%(name)s.%(funcName)s: %(message)s",
>> level=logging.INFO)
>>     f = Felis()
>>     f.alpha()
>>     c = Catus()
>>     c.alpha()
>>     c.beta()
> 
> 
>> 
>> If the metaclass bothers you, here's a simpler alternative:
> 
> simpler to implement but more error prone.  I like the metaclass model.
>   now if one could fill in the class and method name automatically, life
> would be good.

Well, the method name is already there in my example; a class name is there,
too (disguised as the logger name), but it is the name of the actual
instance's class, not the class where the method is defined.
Here is a revised example that logs the defining class:

import logging

class LoggedType(type):
    def __new__(mcl, name, bases, classdict):
        classdict["_%s__logger" % name] = logging.getLogger(name)
        return type.__new__(mcl, name, bases, classdict)

class Logged:
    __metaclass__ = LoggedType

class Felis(Logged):
    def alpha(self):
        self.__logger.info("Felis.alpha")
    def gamma(self):
        self.__logger.info("Felis.gamma")

class Catus(Felis):
    def alpha(self):
        self.__logger.info("Catus.alpha")
    def beta(self):
        self.__logger.info("Catus.beta")

if __name__ == "__main__":
    logging.basicConfig(
        format="EXPECTED %(message)s GOT %(name)s.%(funcName)s",
level=logging.INFO)
    f = Felis()
    f.alpha()
    f.gamma()
    c = Catus()
    c.alpha()
    c.beta()
    c.gamma()

Peter

-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to