Skip to main content

Decorator for logging function arguments and return value by human-readable way

Project description

logwrap

https://travis-ci.com/python-useful-helpers/logwrap.svg?branch=master Azure DevOps builds https://coveralls.io/repos/github/python-useful-helpers/logwrap/badge.svg?branch=master Documentation Status https://img.shields.io/pypi/v/logwrap.svg https://img.shields.io/pypi/pyversions/logwrap.svg https://img.shields.io/pypi/status/logwrap.svg https://img.shields.io/github/license/python-useful-helpers/logwrap.svg https://img.shields.io/badge/code%20style-black-000000.svg

logwrap is a helper for logging in human-readable format function arguments and call result on function call. Why? Because logging of *args, **kwargs become 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 virtualenv

  • Free software: Apache license

  • Open Source: https://github.com/python-useful-helpers/logwrap

  • PyPI packaged: https://pypi.python.org/pypi/logwrap

  • Self-documented code: docstrings with types in comments

  • Tested: see bages on top

  • Support multiple Python versions:

Python 3.6
Python 3.7

This package includes helpers:

  • logwrap - main helper. The same is LogWrap.

  • LogWrap - class with logwrap implementation. May be used directly.

  • pretty_repr

  • pretty_str

  • PrettyFormat

  • LogOnAccess - property with logging on successful get/set/delete or failure.

Usage

logwrap

The 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.

Argumented usage with arguments from signature:

@logwrap.logwrap(
    log=logging.getLogger(__name__),  # __name__ = 'logwrap'
    log_level=logging.DEBUG,
    exc_level=logging.ERROR,
    max_indent=20,  # forwarded to the pretty_repr
    spec=None,  # use target callable function for spec
    blacklisted_names=None,  # list argument names, which should be dropped from log
    blacklisted_exceptions=None,  # Exceptions to skip in log
    log_call_args=True,  # Log call arguments before call
    log_call_args_on_exc=True,  # Log call arguments if exception happens
    log_traceback = True,  # Log traceback if exception happens
    log_result_obj=True,  # Log result object
)

Usage examples:

@logwrap.logwrap()
def foo():
    pass

is equal to:

@logwrap.logwrap
def foo():
    pass

Get decorator for use without parameters:

get_logs = logwrap.logwrap()  # set required parameters via arguments

type(get_logs) == LogWrap  # All logic is implemented in LogWrap class starting from version 2.2.0

@get_logs
def foo():
    pass

Call example:

import logwrap

@logwrap.logwrap
def example_function1(
        arg1: str,
        arg2: str='arg2',
        *args,
        kwarg1: str,
        kwarg2: str='kwarg2',
        **kwargs
) -> tuple():
    return (arg1, arg2, args, kwarg1, kwarg2, kwargs)

example_function1('arg1', kwarg1='kwarg1', kwarg3='kwarg3')

This code during execution will produce log records:

Calling:
'example_function1'(
    # POSITIONAL_OR_KEYWORD:
    'arg1'=u'''arg1''',  # type: <class 'str'>
    'arg2'=u'''arg2''',  # type: <class 'str'>
    # VAR_POSITIONAL:
    'args'=(),
    # KEYWORD_ONLY:
    'kwarg1'=u'''kwarg1''',  # type: <class 'str'>
    'kwarg2'=u'''kwarg2''',  # type: <class 'str'>
    # VAR_KEYWORD:
    'kwargs'=
        dict({
            'kwarg3': u'''kwarg3''',
        }),
)
Done: 'example_function1' with result:

 tuple((
    u'''arg1''',
    u'''arg2''',
    (),
    u'''kwarg1''',
    u'''kwarg2''',
     dict({
        'kwarg3': u'''kwarg3''',
     }),
 ))

LogWrap

Example construction and read from test:

log_call = logwrap.LogWrap()
log_call.log_level == logging.DEBUG
log_call.exc_level == logging.ERROR
log_call.max_indent == 20
log_call.blacklisted_names == []
log_call.blacklisted_exceptions == []
log_call.log_call_args == True
log_call.log_call_args_on_exc == True
log_call.log_traceback == True
log_call.log_result_obj == True

On 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 override pre_process_param and post_process_param.

See API documentation for details.

pretty_repr

This is specified helper for making human-readable repr on complex objects. Signature is self-documenting:

def pretty_repr(
    src,  # object for repr
    indent=0,  # start indent
    no_indent_start=False,  # do not indent the first level
    max_indent=20,  # maximum allowed indent level
    indent_step=4,  # step between indents
)

Limitation: Dict like objects is always marked inside {} for readability, even if it is collections.OrderedDict (standard repr as list of tuples).

pretty_str

This is specified helper for making human-readable str on complex objects. Signature is self-documenting:

def pretty_str(
    src,  # object for __str__
    indent=0,  # start indent
    no_indent_start=False,  # do not indent the first level
    max_indent=20,  # maximum allowed indent level
    indent_step=4,  # step between indents
)
Limitations:

Dict like objects is always marked inside {} for readability, even if it is collections.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__).

PrettyFormat

PrettyFormat is the main formatting implementation class. pretty_repr and pretty_str uses instances of subclasses PrettyRepr and PrettyStr from this class. This class is mostly exposed for typing reasons. Object signature:

def __init__(
    self,
    max_indent=20,  # maximum allowed indent level
    indent_step=4,  # step between indents
)

Callable object (PrettyFormat instance) signature:

def __call__(
    self,
    src,  # object for repr
    indent=0,  # start indent
    no_indent_start=False  # do not indent the first level
)

Adopting your code

pretty_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.

LogOnAccess

This 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 with property, but in addition it is possible to customize logger, log levels and log conditions.

Usage examples:

  1. Simple usage. All by default. logger is re-used from instance if available with names logger or log else used internal logwrap.log_on_access logger:

import logging

class Target(object):

    def init(self, val='ok')
        self.val = val
        self.logger = logging.get_logger(self.__class__.__name__)  # Single for class, follow subclassing

    def __repr__(self):
        return "{cls}(val={self.val})".format(cls=self.__class__.__name__, self=self)

    @logwrap.LogOnAccess
    def ok(self):
        return self.val

    @ok.setter
    def ok(self, val):
        self.val = val

    @ok.deleter
    def ok(self):
        self.val = ""
  1. Use with global logger for class:

class Target(object):

  def init(self, val='ok')
      self.val = val

  def __repr__(self):
      return "{cls}(val={self.val})".format(cls=self.__class__.__name__, self=self)

  @logwrap.LogOnAccess
  def ok(self):
      return self.val

  @ok.setter
  def ok(self, val):
      self.val = val

  @ok.deleter
  def ok(self):
      self.val = ""

  ok.logger = 'test_logger'
  ok.log_level = logging.INFO
  ok.exc_level = logging.ERROR
  ok.log_object_repr = True  # As by default
  ok.log_success = True  # As by default
  ok.log_failure = True  # As by default
  ok.log_traceback = True  # As by default
  ok.override_name = None  # As by default: use original name

Testing

The main test mechanism for the package logwrap is using tox. Available environments can be collected via tox -l

CI systems

For code checking several CI systems is used in parallel:

  1. Travis CI: is used for checking: PEP8, pylint, bandit, installation possibility and unit tests. Also it’s publishes coverage on coveralls.

  2. coveralls: is used for coverage display.

  3. Azure CI: is used for functional tests on Windows.

CD systems

  1. Travis CI: is used for linux and SDIST package delivery on PyPI.

Project details


Release history Release notifications | RSS feed

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

logwrap-6.2.2-cp37-cp37m-win_amd64.whl (176.5 kB view details)

Uploaded CPython 3.7mWindows x86-64

logwrap-6.2.2-cp37-cp37m-win32.whl (153.8 kB view details)

Uploaded CPython 3.7mWindows x86

logwrap-6.2.2-cp36-cp36m-win_amd64.whl (176.3 kB view details)

Uploaded CPython 3.6mWindows x86-64

logwrap-6.2.2-cp36-cp36m-win32.whl (153.7 kB view details)

Uploaded CPython 3.6mWindows x86

File details

Details for the file logwrap-6.2.2-cp37-cp37m-win_amd64.whl.

File metadata

  • Download URL: logwrap-6.2.2-cp37-cp37m-win_amd64.whl
  • Upload date:
  • Size: 176.5 kB
  • Tags: CPython 3.7m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for logwrap-6.2.2-cp37-cp37m-win_amd64.whl
Algorithm Hash digest
SHA256 847dd13a7edda1cc2a77a3a95d4359f9380512175292e839157ade340a00c19c
MD5 c474d090d77d667de3fc3eb72a7e7512
BLAKE2b-256 efeeaedaf40404fb5095211aeb78af9e208ec10202bc37c065d29652d87ca6de

See more details on using hashes here.

File details

Details for the file logwrap-6.2.2-cp37-cp37m-win32.whl.

File metadata

  • Download URL: logwrap-6.2.2-cp37-cp37m-win32.whl
  • Upload date:
  • Size: 153.8 kB
  • Tags: CPython 3.7m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for logwrap-6.2.2-cp37-cp37m-win32.whl
Algorithm Hash digest
SHA256 e30375ab3614e8b4d2f5e1bba5777b3dd06526d47a1da4acdce6898c069dd7ec
MD5 4fd6bc2283b9320c8e570d55b6cedf7d
BLAKE2b-256 3403df21efe527a2c6c867b104a71d0a6ab1e59594eb2cff389ac0c9a1e89b87

See more details on using hashes here.

File details

Details for the file logwrap-6.2.2-cp36-cp36m-win_amd64.whl.

File metadata

  • Download URL: logwrap-6.2.2-cp36-cp36m-win_amd64.whl
  • Upload date:
  • Size: 176.3 kB
  • Tags: CPython 3.6m, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.8

File hashes

Hashes for logwrap-6.2.2-cp36-cp36m-win_amd64.whl
Algorithm Hash digest
SHA256 b45bfaa84659e1f8cb618044260a820547a73107a5792a150fe81fe55ef2350f
MD5 11fdfb50560f1c511453095f235e7ec5
BLAKE2b-256 aa48d2e6062205c23b79d55478debbce7a7c8b2815d9545dc724f6665ed34c6d

See more details on using hashes here.

File details

Details for the file logwrap-6.2.2-cp36-cp36m-win32.whl.

File metadata

  • Download URL: logwrap-6.2.2-cp36-cp36m-win32.whl
  • Upload date:
  • Size: 153.7 kB
  • Tags: CPython 3.6m, Windows x86
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/41.0.1 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.6.8

File hashes

Hashes for logwrap-6.2.2-cp36-cp36m-win32.whl
Algorithm Hash digest
SHA256 673a742041e040c52f0bf119a4943b4a670e47030b242a5417bc656b89085746
MD5 8d49d9ea1cf7ec3e0c9a1d17c2bc8c4c
BLAKE2b-256 71fcc67e5f181d62c6e62d27344980091174acaf7b8758756b7280ceb4609b7a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page