Package: logwrap
logwraplogwrap is a helper for logging in human-readable format function arguments and call result on function call.
Why? Because logging of*args, **kwargsbecome useless with project grow and you need more details in call log.Cons:Log records are not single line.Pros:Log records are not single 100500 symbols length line.
(Especially actual for testing/development environments and for Kibana users).Service free: job is done by this library and it’s dependencies. It works at virtualenvFree software: Apache licenseOpen Source:https://github.com/python-useful-helpers/logwrapPyPI packaged:https://pypi.python.org/pypi/logwrapSelf-documented code: docstrings with types in commentsTested: see bages on topSupport multiple Python versions:Python 3.8
Python 3.9
Python 3.10NoteSupport python versions:2.7: < 5.0.03.5: < 6.0.03.7: < 10.0.0This package includes helpers:logwrap- main helper. The same isLogWrap.LogWrap- class withlogwrapimplementation. May be used directly.pretty_reprpretty_strPrettyFormatLogOnAccess- property with logging on successful get/set/delete or failure.UsagelogwrapThe main decorator. Could be used as not argumented (@logwrap.logwrap) and argumented (@logwrap.logwrap()).
Not argumented usage simple calls with default values for all positions.NoteArgumens should be set via keywords only.Argumented usage with arguments from signature:@logwrap.logwrap(log=None,# if not set: try to find LOGGER, LOG, logger or log object in target module and use it if it logger instance. Fallback: logger named logwraplog_level=logging.DEBUG,exc_level=logging.ERROR,max_indent=20,# forwarded to the pretty_reprblacklisted_names=None,# list argument names, which should be dropped from logblacklisted_exceptions=None,# Exceptions to skip details in log (no traceback, no exception details - just class name)log_call_args=True,# Log call arguments before calllog_call_args_on_exc=True,# Log call arguments if exception happenslog_traceback=True,# Log traceback if exception happenslog_result_obj=True,# Log result object)Usage examples:@logwrap.logwrap()deffoo():passis equal to:@logwrap.logwrapdeffoo():passGet decorator for use without parameters:get_logs=logwrap.logwrap()# set required parameters via argumentstype(get_logs)==LogWrap# All logic is implemented in LogWrap class starting from version 2.2.0@get_logsdeffoo():passCall example (python 3.8):importlogwrap@logwrap.logwrapdefexample_function1(arg0:str,/,arg1:str,arg2:str='arg2',*args,kwarg1:str,kwarg2:str='kwarg2',**kwargs)->tuple():return(arg0,arg1,arg2,args,kwarg1,kwarg2,kwargs)example_function1('arg0','arg1',kwarg1='kwarg1',kwarg3='kwarg3')This code during execution will produce log records:Calling:
'example_function1'(
    # POSITIONAL_ONLY:
    arg0='arg0',  # type: str
    # POSITIONAL_OR_KEYWORD:
    arg1='arg1',  # type: str
    arg2='arg2',  # type: str
    # VAR_POSITIONAL:
    args=(),
    # KEYWORD_ONLY:
    kwarg1='kwarg1',  # type: str
    kwarg2='kwarg2',  # type: str
    # VAR_KEYWORD:
    kwargs={
        'kwarg3': 'kwarg3',
    },
)
Done: 'example_function1' with result:

 (
    'arg0',
    'arg1',
    'arg2',
    (),
    'kwarg1',
    'kwarg2',
    {
        'kwarg3': 'kwarg3',
    },
 )LogWrapExample construction and read from test:log_call=logwrap.LogWrap()log_call.log_level==logging.DEBUGlog_call.exc_level==logging.ERRORlog_call.max_indent==20log_call.blacklisted_names==[]log_call.blacklisted_exceptions==[]log_call.log_call_args==Truelog_call.log_call_args_on_exc==Truelog_call.log_traceback==Truelog_call.log_result_obj==TrueOn object change, variable types is validated.In special cases, when special processing required for parameters logging (hide or change parameters in log),
it can be done by overridepre_process_paramandpost_process_param.See API documentation for details.pretty_reprThis is specified helper for making human-readable repr on complex objects.
Signature is self-documenting:defpretty_repr(src,# object for reprindent=0,# start indentno_indent_start=False,# do not indent the first levelmax_indent=20,# maximum allowed indent levelindent_step=4,# step between indents)pretty_strThis is specified helper for making human-readable str on complex objects.
Signature is self-documenting:defpretty_str(src,# object for __str__indent=0,# start indentno_indent_start=False,# do not indent the first levelmax_indent=20,# maximum allowed indent levelindent_step=4,# step between indents)Limitations:Dict like objects is always marked inside{}for readability, even if it iscollections.OrderedDict(standard repr as list of tuples).Iterable types is not declared, only brackets is used.String and bytes looks the same (its __str__, not __repr__).PrettyFormatPrettyFormat is the main formatting implementation class.pretty_reprandpretty_struses instances of subclassesPrettyReprandPrettyStrfrom this class.
This class is mostly exposed for typing reasons.
Object signature:def__init__(self,max_indent=20,# maximum allowed indent levelindent_step=4,# step between indents)Callable object (PrettyFormatinstance) signature:def__call__(self,src,# object for reprindent=0,# start indentno_indent_start=False# do not indent the first level)Adopting your codepretty_repr behavior could be overridden for your classes by implementing specific magic method:def__pretty_repr__(self,parser# PrettyFormat class instance,indent# start indent,no_indent_start# do not indent the first level):return...This method will be executed instead of __repr__ on your object.def__pretty_str__(self,parser# PrettyFormat class instance,indent# start indent,no_indent_start# do not indent the first level):return...This method will be executed instead of __str__ on your object.LogOnAccessThis special case of property is useful in cases, where a lot of properties should be logged by similar way without writing a lot of code.Basic API is conform withproperty, but in addition it is possible to customize logger, log levels and log conditions.Usage examples:Simple usage. All by default.
logger is re-used:from instance if available with namesloggerorlog,from instance module if available with namesLOGGER,log,else used internallogwrap.log_on_accesslogger.importloggingclassTarget(object):definit(self,val='ok')self.val=valself.logger=logging.get_logger(self.__class__.__name__)# Single for class, follow subclassingdef__repr__(self):return"{cls}(val={self.val})".format(cls=self.__class__.__name__,self=self)@logwrap.LogOnAccessdefok(self):returnself.val@ok.setterdefok(self,val):self.val=val@ok.deleterdefok(self):self.val=""Use with global logger for class:classTarget(object):definit(self,val='ok')self.val=valdef__repr__(self):return"{cls}(val={self.val})".format(cls=self.__class__.__name__,self=self)@logwrap.LogOnAccessdefok(self):returnself.val@ok.setterdefok(self,val):self.val=val@ok.deleterdefok(self):self.val=""ok.logger='test_logger'ok.log_level=logging.INFOok.exc_level=logging.ERRORok.log_object_repr=True# As by defaultok.log_before=True# As by defaultok.log_success=True# As by defaultok.log_failure=True# As by defaultok.log_traceback=True# As by defaultok.override_name=None# As by default: use original nameTestingThe main test mechanism for the packagelogwrapis usingtox.
Available environments can be collected viatox -lCI/CD systemsGitHub:is used for functional tests.