code: def logx(supress_args=[], supress_all_args=False, supress_result=False, logger=logging.getLogger(),
           debug_level=logging.DEBUG):
      
  
      def decorator(fn):
          def func(*args, **kwargs):
              if not supress_all_args:
                  arg_string = ""
                  for i in range(0, len(args)):
                      var_name = fn.__code__.co_varnames[i]
                      if var_name != "self" and var_name not in supress_args:
                          arg_string += var_name + ":" + str(args[i]) + ","
                  arg_string = arg_string[0:len(arg_string) - 1]
                  string = (RED + BOLD + '>> ' + END + 'Calling {0}({1})'.format(fn.__name__, arg_string))
                  if len(kwargs):
                      string = (
                          RED + BOLD + '>> ' + END + 'Calling {0} with args {1} and kwargs {2}'.format(
                              fn.__name__,
                              arg_string, kwargs))
                  logger.log(debug_level, string)
  
              wrapped_fn = fn
              if not asyncio.iscoroutine(fn):
                  wrapped_fn = asyncio.coroutine(fn)
              result = yield from wrapped_fn(*args, **kwargs)
  
              if not supress_result:
                  string = BLUE + BOLD + '<< ' + END + 'Return {0} with result : {1}'.format(fn.__name__, result)
                  logger.log(debug_level, string)
              return result
  
          return func
  
      return decorator
docs: logs parameters and result
      takes arguments
          supress_args - list of parameter names to supress
          supress_all_args - boolean to supress all arguments
          supress_result - boolean to supress result
          receiver - custom logging function which takes a string as input; defaults to logging on stdout