1*9c5db199SXin Li#!/usr/bin/env python 2*9c5db199SXin Li 3*9c5db199SXin Li# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 4*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 5*9c5db199SXin Li# found in the LICENSE file. 6*9c5db199SXin Li 7*9c5db199SXin Liimport functools 8*9c5db199SXin Liimport inspect 9*9c5db199SXin Liimport logging 10*9c5db199SXin Liimport traceback 11*9c5db199SXin Li 12*9c5db199SXin Lidef log_dbus_method(return_logger=logging.debug, raise_logger=logging.warning, 13*9c5db199SXin Li return_cb_arg=None, raise_cb_arg=None): 14*9c5db199SXin Li """ 15*9c5db199SXin Li Factory method for decorator to log dbus responses / errors. 16*9c5db199SXin Li 17*9c5db199SXin Li This method should be used to decorate the most concerete implementation of 18*9c5db199SXin Li a dbus method. 19*9c5db199SXin Li 20*9c5db199SXin Li @param return_logger: A function that accepts a string argument to log the 21*9c5db199SXin Li response from the decorated function. 22*9c5db199SXin Li @param raise_logger: A function accepts a string argument to log the 23*9c5db199SXin Li exception raised by the decorated function. 24*9c5db199SXin Li @param return_cb_arg: str name of the async callback argument for the return 25*9c5db199SXin Li value, if the function takes one. 26*9c5db199SXin Li @param raise_cb_arg: str name of the async callback argument for the error 27*9c5db199SXin Li return value, if the function takes one. 28*9c5db199SXin Li 29*9c5db199SXin Li """ 30*9c5db199SXin Li def wrapper(func): 31*9c5db199SXin Li """ 32*9c5db199SXin Li The decorator returned by this factory. 33*9c5db199SXin Li 34*9c5db199SXin Li @param func: The function to be decorated. 35*9c5db199SXin Li 36*9c5db199SXin Li """ 37*9c5db199SXin Li @functools.wraps(func) 38*9c5db199SXin Li def wrapped_func(*args, **kwargs): 39*9c5db199SXin Li """The modified function for the decorated function.""" 40*9c5db199SXin Li modified_args = list(args) 41*9c5db199SXin Li modified_kwargs = kwargs 42*9c5db199SXin Li return_cb_index = getattr(wrapped_func, '_logging_return_cb_index') 43*9c5db199SXin Li if return_cb_index > -1: 44*9c5db199SXin Li if len(args) > return_cb_index: 45*9c5db199SXin Li modified_args[return_cb_index] = _wrap_async_return( 46*9c5db199SXin Li args[return_cb_index], 47*9c5db199SXin Li func.__name__, 48*9c5db199SXin Li return_logger) 49*9c5db199SXin Li elif return_cb_arg in kwargs: 50*9c5db199SXin Li modified_kwargs[return_cb_arg] = _wrap_async_return( 51*9c5db199SXin Li kwargs[return_cb_arg], 52*9c5db199SXin Li func.__name__, 53*9c5db199SXin Li return_logger) 54*9c5db199SXin Li else: 55*9c5db199SXin Li logging.debug('Not logging default return_cb') 56*9c5db199SXin Li 57*9c5db199SXin Li raise_cb_index = getattr(wrapped_func, '_logging_raise_cb_index') 58*9c5db199SXin Li if raise_cb_index > -1: 59*9c5db199SXin Li if len(args) > raise_cb_index: 60*9c5db199SXin Li modified_args[raise_cb_index] = _wrap_async_raise( 61*9c5db199SXin Li args[raise_cb_index], 62*9c5db199SXin Li func.__name__, 63*9c5db199SXin Li raise_logger) 64*9c5db199SXin Li elif raise_cb_arg in kwargs: 65*9c5db199SXin Li modified_kwargs[raise_cb_arg] = _wrap_async_raise( 66*9c5db199SXin Li kwargs[raise_cb_arg], 67*9c5db199SXin Li func.__name__, 68*9c5db199SXin Li raise_logger) 69*9c5db199SXin Li else: 70*9c5db199SXin Li logging.debug('Not logging default raise_cb') 71*9c5db199SXin Li 72*9c5db199SXin Li try: 73*9c5db199SXin Li retval = func(*modified_args, **modified_kwargs) 74*9c5db199SXin Li # No |return_cb_arg| ==> return value is the DBus response, so 75*9c5db199SXin Li # it needs to be logged. 76*9c5db199SXin Li if return_cb_index == -1: 77*9c5db199SXin Li return_logger('Response[%s] OK: |%s|' % (func.__name__, 78*9c5db199SXin Li repr(retval))) 79*9c5db199SXin Li except Exception as e: 80*9c5db199SXin Li raise_logger('Response[%s] ERROR: |%s|' % (func.__name__, 81*9c5db199SXin Li repr(e))) 82*9c5db199SXin Li raise_logger(traceback.format_exc()) 83*9c5db199SXin Li raise 84*9c5db199SXin Li return retval 85*9c5db199SXin Li 86*9c5db199SXin Li 87*9c5db199SXin Li args, _, _, defaults = inspect.getargspec(func) 88*9c5db199SXin Li wrapped_func._logging_return_cb_index = -1 89*9c5db199SXin Li wrapped_func._logging_raise_cb_index = -1 90*9c5db199SXin Li if return_cb_arg: 91*9c5db199SXin Li if return_cb_arg not in args: 92*9c5db199SXin Li logging.warning( 93*9c5db199SXin Li 'Did not find expected argument %s in argument list ' 94*9c5db199SXin Li 'of %s', return_cb_arg, func.__name__) 95*9c5db199SXin Li wrapped_func._logging_return_cb_index = args.index(return_cb_arg) 96*9c5db199SXin Li if raise_cb_arg: 97*9c5db199SXin Li if raise_cb_arg not in args: 98*9c5db199SXin Li logging.warning( 99*9c5db199SXin Li 'Did not find expected argument %s in argument list ' 100*9c5db199SXin Li 'of %s', raise_cb_arg, func.__name__) 101*9c5db199SXin Li wrapped_func._logging_raise_cb_index = args.index(raise_cb_arg) 102*9c5db199SXin Li return wrapped_func 103*9c5db199SXin Li return wrapper 104*9c5db199SXin Li 105*9c5db199SXin Li 106*9c5db199SXin Lidef _wrap_async_return(return_cb, fname, logger): 107*9c5db199SXin Li """ 108*9c5db199SXin Li Wrap return_cb to log the return value. 109*9c5db199SXin Li 110*9c5db199SXin Li @param return_cb: The function to be wrapped. 111*9c5db199SXin Li @param fname: Name of the DBus function called. 112*9c5db199SXin Li @param logger: The logger to use for logging. 113*9c5db199SXin Li @returns: Wrapped |return_cb| that additionally logs its arguments. 114*9c5db199SXin Li 115*9c5db199SXin Li """ 116*9c5db199SXin Li @functools.wraps(return_cb) 117*9c5db199SXin Li def wrapped_return_cb(*args, **kwargs): 118*9c5db199SXin Li """ Log arguments before calling return_cb. """ 119*9c5db199SXin Li logger('AsyncResponse[%s] OK: |%s|' % (fname, str((args, kwargs)))) 120*9c5db199SXin Li return_cb(*args, **kwargs) 121*9c5db199SXin Li 122*9c5db199SXin Li return wrapped_return_cb 123*9c5db199SXin Li 124*9c5db199SXin Li 125*9c5db199SXin Lidef _wrap_async_raise(raise_cb, fname, logger): 126*9c5db199SXin Li """ 127*9c5db199SXin Li Wrap raise_cb to log the raised error. 128*9c5db199SXin Li 129*9c5db199SXin Li @param raise_cb: The function to be wrapped. 130*9c5db199SXin Li @param fname: Name of the DBus function called. 131*9c5db199SXin Li @param logger: The logger to use for logging. 132*9c5db199SXin Li @returns: Wrapped |raise_cb| that additionally logs its arguments. 133*9c5db199SXin Li 134*9c5db199SXin Li """ 135*9c5db199SXin Li @functools.wraps(raise_cb) 136*9c5db199SXin Li def wrapped_raise_cb(*args, **kwargs): 137*9c5db199SXin Li """ Log arguments before calling raise_cb. """ 138*9c5db199SXin Li logger('AsyncResponse[%s] ERROR: |%s|' % (fname, str((args, kwargs)))) 139*9c5db199SXin Li logger(traceback.format_exc()) 140*9c5db199SXin Li raise_cb(*args, **kwargs) 141*9c5db199SXin Li 142*9c5db199SXin Li return wrapped_raise_cb 143