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