Giter Site home page Giter Site logo

python-useful-helpers / logwrap Goto Github PK

View Code? Open in Web Editor NEW
12.0 3.0 2.0 1.06 MB

logwrap is a helper for logging in human-readable format function arguments and call result on function call

License: Apache License 2.0

Python 100.00%
log-message decorator python python-3 pretty-print py3 python3

logwrap's Introduction

logwrap

image

image

Documentation Status

image

image

image

image

image

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

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.

Note

Argumens 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 logwrap
    log_level=logging.DEBUG,
    exc_level=logging.ERROR,
    max_indent=20,  # forwarded to the pretty_repr
    blacklisted_names=None,  # list argument names, which should be dropped from log
    blacklisted_exceptions=None,  # Exceptions to skip details in log (no traceback, no exception details - just class name)
    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 (python 3.8):

import logwrap

@logwrap.logwrap
def example_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',
    },
 )

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
)

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,
    • from instance module if available with names LOGGER, 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_before = 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/CD systems

GitHub: is used for functional tests.

logwrap's People

Contributors

azure-pipelines[bot] avatar dependabot[bot] avatar penguinolog avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

logwrap's Issues

logwrap breaks functions

Using logwrap breaks functions. When I add @logwrap.logwrap to get_shape function it stops working:

In [1]: from telluric import GeoVector, constants
In [2]: from shapely import geometry
In [3]: from rasterio.crs import CRS
In [4]: poly = geometry.Polygon.from_bounds(4334065.7375318575650454, 5627100.9060072815045714, 4337359.6036174902692437,5630885.4445580849424005)
In [5]: roi = GeoVector(poly, crs=constants.WEB_MERCATOR_CRS)
In [6]: roi.get_shape(CRS({'init': 'epsg:3857'}))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-6-0be1dd22719f> in <module>()
----> 1 roi.get_shape(CRS({'init': 'epsg:3857'}))

~/sandbox/telluric/env/lib/python3.5/site-packages/logwrap/_log_wrap.cpython-35m-x86_64-linux-gnu.so in logwrap._log_wrap.LogWrap._get_function_wrapper.wrapper()

~/sandbox/telluric/env/lib/python3.5/site-packages/logwrap/_log_wrap.cpython-35m-x86_64-linux-gnu.so in bind_args_kwargs()

~/sandbox/telluric/telluric/telluric/vectors.py in __eq__(self, other)
    437         # otherwise the delegation won't happen
    438         return (
--> 439             self.crs == other.crs
    440             and self._shape.equals(other.get_shape(self.crs))
    441         )

AttributeError: type object '_empty' has no attribute 'crs'

Without @logwrap.logwrap all works as expected.

Truncation of large objects

Thanks for the library! One problem I tend to have with it is that sometimes a function argument will be something very large, like a list of 1000s of strings, and it would still be nice to log this function call with logwrap, but it just clutters the log. I think a nice solution would be to add a max_iterable_length parameter similar to the max_indent parameter we already have.

Deprecate Python 3.4

Python 3.4 was not super popular and popularity drops day-by-day.
Drop of support will make code of Python 3 branch less complex.

  • SLES 12 uses Python 3.4.5, but it's not installed by default. Should use old version or update.
  • RHEL7 - Python 3.6 in EPEL repo
  • Ubuntu 16.04 - released with python 3.5

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.