The autologging API¶
- autologging.TRACE = 1¶
A custom tracing log level, lower in severity than logging.DEBUG. Autologging traced() and TracedMethods() create log records with this custom level.
- autologging.logged(obj)[source]¶
Add a named logger member to a decorated class or function.
The logger member always has a dot-separated name consisting of the parent logger’s name, followed by a dot (‘.’), followed by the class or function name.
If obj is a class, then obj.__logger will have the logger name “module-name.class-name”:
>>> @logged ... class Test: ... pass ... >>> Test._Test__logger.name 'autologging.Test'
Similarly for functions:
>>> @logged ... def test(): ... pass ... >>> test.__logger.name 'autologging.test'
If obj is a logging.Logger object, then that logger is treated as the parent logger and the decorated class’s __logger member will have the logger name “parent-logger-name.class-name”:
>>> _logger = logging.getLogger("test.parent") >>> @logged(_logger) ... class Test: ... pass ... >>> Test._Test__logger.name 'test.parent.Test'
Again, functions are similar.
>>> _logger = logging.getLogger("test.parent") >>> @logged(_logger) ... def test_fn(): ... pass ... >>> test_fn.__logger.name 'test.parent.test_fn'
Note
For classes, the logger member is made “private” (i.e. __logger with double underscore) to ensure that log messages that include the %(name)s format placeholder are written with the correct name.
Consider a subclass of a @logged-decorated parent class. If the subclass were not decorated with @logged and could access the parent’s logger member directly to make logging calls, those log messages would display the name of the parent class, not the subclass.
Therefore, subclasses of a @logged-decorated parent class that wish to use a provided self.__logger object must themselves be decorated with @logged.
Note
Within a logged function, the __logger attribute must be qualified by the function name, i.e. “function-name.__logger”:
>>> @logged ... def do_something(): ... do_something.__logger.info('Doing something') ...
- autologging.traced(obj)[source]¶
Add call/return tracing to an unbound function.
Warning
This decorator will not quite work as expected (or may fail entirely) for class methods. To automatically trace class method call/return, please see the TracedMethods metaclass factory.
In the following example, tracing log messages are written to a log whose channel defaults to the function’s module name:
>>> import sys >>> logging.basicConfig(level=TRACE, stream=sys.stdout) >>> @traced ... def my_function(arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... >>> my_function("spam", keyword="eggs") TRACE:autologging:CALL my_function *('spam',) **{'keyword': 'eggs'} TRACE:autologging:RETURN my_function 'spam and eggs' 'spam and eggs'
In the following example, tracing log messages are written to a user-named log:
>>> import sys >>> logging.basicConfig(level=TRACE, stream=sys.stdout) >>> _logger = logging.getLogger("test.ing") >>> @traced(_logger) ... def my_function(arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... >>> my_function("spam", keyword="eggs") TRACE:test.ing:CALL my_function *('spam',) **{'keyword': 'eggs'} TRACE:test.ing:RETURN my_function 'spam and eggs' 'spam and eggs'
- autologging.TracedMethods(*args)[source]¶
Return a metaclass that enables call/return tracing for methods.
Only methods named explicitly in args will be traced.
If the first item in args is a logging.Logger, then that logger is treated as the parent logger, and method tracers will use a logger with the name “parent-logger-name.class-name”. Otherwise, method tracers will use a logger with the name “module-name.class-name”.
Note
Regardless of whether a logger was explicitly passed in or not, the logger used by the tracers is not made available to the class or its instances.
This allows for logging configurations where tracing is sent to a separate file/target than logging. For such a configuration, simply configure a logger specifically for tracing and pass that logger as the first argument to TracedMethods.
Also note that a class may be decorated by logged() and use TracedMethods as a metaclass without conflict.
In the following example, tracing log messages are written to a log whose channel defaults to “module-name.class-name”:
>>> import sys >>> logging.basicConfig(level=TRACE, stream=sys.stdout) >>> class MyClass(object, ... metaclass=TracedMethods("my_staticmethod", ... "my_classmethod", ... "my_instancemethod")): ... @staticmethod ... def my_staticmethod(arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... @classmethod ... def my_classmethod(cls, arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... def my_instancemethod(self, arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... >>> MyClass.my_staticmethod("spam", keyword="eggs") TRACE:autologging.MyClass:CALL MyClass.my_staticmethod *('spam',) **{'keyword': 'eggs'} TRACE:autologging.MyClass:RETURN MyClass.my_staticmethod 'spam and eggs' 'spam and eggs' >>> MyClass.my_classmethod("green eggs", keyword="ham") TRACE:autologging.MyClass:CALL MyClass.my_classmethod *('green eggs',) **{'keyword': 'ham'} TRACE:autologging.MyClass:RETURN MyClass.my_classmethod 'green eggs and ham' 'green eggs and ham' >>> instance = MyClass() >>> instance.my_instancemethod("Batman", keyword="Robin") TRACE:autologging.MyClass:CALL MyClass.my_instancemethod *('Batman',) **{'keyword': 'Robin'} TRACE:autologging.MyClass:RETURN MyClass.my_instancemethod 'Batman and Robin' 'Batman and Robin'
In the following example, tracing log messages are written to a user-named log:
>>> import sys >>> logging.basicConfig(level=TRACE, stream=sys.stdout) >>> _logger = logging.getLogger("test.ing") >>> class MyClass(object, ... metaclass=TracedMethods(_logger, "my_staticmethod", ... "my_classmethod", ... "my_instancemethod")): ... @staticmethod ... def my_staticmethod(arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... @classmethod ... def my_classmethod(cls, arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... def my_instancemethod(self, arg, keyword=None): ... return "%s and %s" % (arg, keyword) ... >>> MyClass.my_staticmethod("spam", keyword="eggs") TRACE:test.ing.MyClass:CALL MyClass.my_staticmethod *('spam',) **{'keyword': 'eggs'} TRACE:test.ing.MyClass:RETURN MyClass.my_staticmethod 'spam and eggs' 'spam and eggs' >>> MyClass.my_classmethod("green eggs", keyword="ham") TRACE:test.ing.MyClass:CALL MyClass.my_classmethod *('green eggs',) **{'keyword': 'ham'} TRACE:test.ing.MyClass:RETURN MyClass.my_classmethod 'green eggs and ham' 'green eggs and ham' >>> instance = MyClass() >>> instance.my_instancemethod("Batman", keyword="Robin") TRACE:test.ing.MyClass:CALL MyClass.my_instancemethod *('Batman',) **{'keyword': 'Robin'} TRACE:test.ing.MyClass:RETURN MyClass.my_instancemethod 'Batman and Robin' 'Batman and Robin'