The autologging API¶
| Release: | 1.0.0 |
|---|
-
autologging.TRACE= 1¶ A custom tracing log level, lower in severity than
logging.DEBUG.
-
autologging.logged(obj)[source]¶ Add a logger member to a decorated class or function.
Parameters: obj – the class or function object being decorated, or an optional logging.Loggerobject to be used as the parent logger (instead of the default module-named logger)Returns: obj if obj is a class or function; otherwise, if obj is a logger, returns a lambda decorator that will in turn set the logger attribute and return obj If obj is a
class, thenobj.__logwill have the logger name “<module-name>.<class-name>”:>>> import sys >>> logging.basicConfig( ... level=logging.DEBUG, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @logged ... class Sample: ... ... def test(self): ... self.__log.debug("This is a test.") ... >>> Sample().test() DEBUG:autologging.Sample:test:This is a test.
Note
Autologging will prefer to use the class’s
__qualname__when it is available (Python 3.3+). Otherwise, the class’s__name__is used. For example:class Outer: @logged class Nested: pass
Under Python 3.3+,
Nested.__logwill have the name “autologging.Outer.Nested”, while under Python 2.7 or 3.2, the logger name will be “autologging.Nested”.Changed in version 0.4.0: Functions decorated with
@loggeduse a single underscore in the logger variable name (e.g.my_function._log) rather than a double underscore.If obj is a function, then
obj._logwill have the logger name “<module-name>”:>>> import sys >>> logging.basicConfig( ... level=logging.DEBUG, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @logged ... def test(): ... test._log.debug("This is a test.") ... >>> test() DEBUG:autologging:test:This is a test.
Note
Within a logged function, the
_logattribute must be qualified by the function name.If obj is a
logging.Loggerobject, then that logger is used as the parent logger (instead of the default module-named logger):>>> import sys >>> logging.basicConfig( ... level=logging.DEBUG, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @logged(logging.getLogger("test.parent")) ... class Sample: ... def test(self): ... self.__log.debug("This is a test.") ... >>> Sample().test() DEBUG:test.parent.Sample:test:This is a test.
Again, functions are similar:
>>> import sys >>> logging.basicConfig( ... level=logging.DEBUG, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @logged(logging.getLogger("test.parent")) ... def test(): ... test._log.debug("This is a test.") ... >>> test() DEBUG:test.parent:test:This is a test.
Note
For classes, the logger member is made “private” (i.e.
__logwith 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@loggedand 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 providedself.__logobject must themselves be decorated with@logged.Warning
Although the
@loggedand@traceddecorators will “do the right thing” regardless of the order in which they are applied to the same function, it is recommended that@loggedalways be used as the innermost decorator:@traced @logged def my_function(): my_function._log.info("message")
This is because
@loggedsimply sets the_logattribute and then returns the original function, making it “safe” to use in combination with any other decorator.
-
autologging.traced(*args)[source]¶ Add call and return tracing to an unbound function or to the methods of a class.
The arguments to
traceddiffer depending on whether it is being used to trace an unbound function or the methods of a class:Trace an unbound function using the default logger
Parameters: func – the unbound function to be traced By default, a logger named for the function’s module is used:
>>> import sys >>> logging.basicConfig( ... level=TRACE, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @traced ... def func(x, y): ... return x + y ... >>> func(7, 9) TRACE:autologging:func:CALL *(7, 9) **{} TRACE:autologging:func:RETURN 16 16
Trace an unbound function using a named logger
Parameters: logger (logging.Logger) – the parent logger used to trace the unbound function >>> import sys >>> logging.basicConfig( ... level=TRACE, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @traced(logging.getLogger("my.channel")) ... def func(x, y): ... return x + y ... >>> func(7, 9) TRACE:my.channel:func:CALL *(7, 9) **{} TRACE:my.channel:func:RETURN 16 16
Trace default methods using the default logger
Parameters: class – the class whose methods will be traced By default, all “public”, “_nonpublic”, and “__internal” methods, as well as the special “__init__” method, will be traced. Tracing log entries will be written to a logger named for the module and class:
>>> import sys >>> logging.basicConfig( ... level=TRACE, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @traced ... class Class: ... def __init__(self, x): ... self._x = x ... def public(self, y): ... return self._x + y ... def _nonpublic(self, y): ... return self._x - y ... def __internal(self, y=2): ... return self._x ** y ... def __repr__(self): ... return "Class(%r)" % self._x ... >>> obj = Class(7) TRACE:autologging.Class:__init__:CALL *(7,) **{} >>> obj.public(9) TRACE:autologging.Class:public:CALL *(9,) **{} TRACE:autologging.Class:public:RETURN 16 16 >>> obj._nonpublic(5) TRACE:autologging.Class:_nonpublic:CALL *(5,) **{} TRACE:autologging.Class:_nonpublic:RETURN 2 2 >>> obj._Class__internal(y=3) TRACE:autologging.Class:__internal:CALL *() **{'y': 3} TRACE:autologging.Class:__internal:RETURN 343 343 >>> repr(obj) # not traced by default 'Class(7)'
Note
When the runtime Python version is >= 3.3, the qualified class name will be used to name the tracing logger (i.e. a nested class will write tracing log entries to a logger named “module.Parent.Nested”).
Trace default methods using a named logger
Parameters: logger (logging.Logger) – the parent logger used to trace the methods of the class By default, all “public”, “_nonpublic”, and “__internal” methods, as well as the special “__init__” method, will be traced. Tracing log entries will be written to the specified logger:
>>> import sys >>> logging.basicConfig( ... level=TRACE, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @traced(logging.getLogger("my.channel")) ... class Class: ... def __init__(self, x): ... self._x = x ... def public(self, y): ... return self._x + y ... def _nonpublic(self, y): ... return self._x - y ... def __internal(self, y=2): ... return self._x ** y ... def __repr__(self): ... return "Class(%r)" % self._x ... >>> obj = Class(7) TRACE:my.channel.Class:__init__:CALL *(7,) **{} >>> obj.public(9) TRACE:my.channel.Class:public:CALL *(9,) **{} TRACE:my.channel.Class:public:RETURN 16 16 >>> obj._nonpublic(5) TRACE:my.channel.Class:_nonpublic:CALL *(5,) **{} TRACE:my.channel.Class:_nonpublic:RETURN 2 2 >>> obj._Class__internal(y=3) TRACE:my.channel.Class:__internal:CALL *() **{'y': 3} TRACE:my.channel.Class:__internal:RETURN 343 343 >>> repr(obj) # not traced by default 'Class(7)'
Trace specified methods using the default logger
Parameters: method_names (tuple) – the names of the methods that will be traced Tracing log entries will be written to a logger named for the module and class:
>>> import sys >>> logging.basicConfig( ... level=TRACE, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @traced("public", "__internal") ... class Class: ... def __init__(self, x): ... self._x = x ... def public(self, y): ... return self._x + y ... def _nonpublic(self, y): ... return self._x - y ... def __internal(self, y=2): ... return self._x ** y ... def __repr__(self): ... return "Class(%r)" % self._x ... >>> obj = Class(7) >>> obj.public(9) TRACE:autologging.Class:public:CALL *(9,) **{} TRACE:autologging.Class:public:RETURN 16 16 >>> obj._nonpublic(5) 2 >>> obj._Class__internal(y=3) TRACE:autologging.Class:__internal:CALL *() **{'y': 3} TRACE:autologging.Class:__internal:RETURN 343 343 >>> repr(obj) 'Class(7)'
Warning
When method names are specified explicitly via args, Autologging ensures that each method is actually defined in the body of the class being traced. (This means that inherited methods that are not overridden are never traced, even if they are named explicitly in args.)
If a defintion for any named method is not found in the class body, either because the method is inherited or because the name is misspelled, Autologging will issue a
UserWarning.If you wish to trace a method from a super class, you have two options:
- Use
tracedto decorate the super class. - Override the method and trace it in the subclass.
Note
When the runtime Python version is >= 3.3, the qualified class name will be used to name the tracig logger (i.e. a nested class will write tracing log entries to a logger named “module.Parent.Nested”).
Trace specified methods using a named logger
Parameters: - logger (logging.Logger) – the parent logger used to trace the methods of the class
- method_names (tuple) – the names of the methods that will be traced
>>> import sys >>> logging.basicConfig( ... level=TRACE, stream=sys.stdout, ... format="%(levelname)s:%(name)s:%(funcName)s:%(message)s") >>> @traced(logging.getLogger("my.channel"), "public", "__internal") ... class Class: ... def __init__(self, x): ... self._x = x ... def public(self, y): ... return self._x + y ... def _nonpublic(self, y): ... return self._x - y ... def __internal(self, y=2): ... return self._x ** y ... def __repr__(self): ... return "Class(%r)" % self._x ... >>> obj = Class(7) >>> obj.public(9) TRACE:my.channel.Class:public:CALL *(9,) **{} TRACE:my.channel.Class:public:RETURN 16 16 >>> obj._nonpublic(5) 2 >>> obj._Class__internal(y=3) TRACE:my.channel.Class:__internal:CALL *() **{'y': 3} TRACE:my.channel.Class:__internal:RETURN 343 343 >>> repr(obj) # not traced by default 'Class(7)'
Warning
When method names are specified explicitly via args, Autologging ensures that each method is actually defined in the body of the class being traced. (This means that inherited methods that are not overridden are never traced, even if they are named explicitly in args.)
If a defintion for any named method is not found in the class body, either because the method is inherited or because the name is misspelled, Autologging will issue a
UserWarning.If you wish to trace a method from a super class, you have two options:
- Use
tracedto decorate the super class. - Override the method and trace it in the subclass.
Note
When tracing a class, if the default (class-named) logger is used and the runtime Python version is >= 3.3, then the qualified class name will be used to name the tracig logger (i.e. a nested class will write tracing log entries to a logger named “module.Parent.Nested”).
Note
If method names are specified when decorating a function, a
UserWarningis issued, but the methods names are ignored and the function is traced as though the method names had not been specified.- Use