我正在尝试编写干净的代码,所以我不希望我的代码被随机记录污染。对我来说,这看起来很糟糕:
def generate_auth_token(self):
logger.debug("Existing auth token: {}".format(self.auth_token))
self.auth_token = uuid.uuid4()
self.save()
logger.debug("Updated auth token: {}".format(self.auth_token))
return str(self.auth_token)
然后,日志如下所示:
backend-api_1 | DEBUG: Device token: 66f4b515-c6f5-433c-885f-61c8a1f63ce5
backend-api_1 | Debug: Existing auth token: 66f4b515-c6f5-433c-885f-61c8a1f63ce5
backend-api_1 | Debug: Updated auth token: 66f4b515-c6f5-433c-885f-61c8a1f63ce5
很难读取这样的代码。我想到了使用装饰器记录每个功能的想法。
def log_input_output(logger_name='', func_name=None, log_input_values=True):
logger = logging.getLogger(logger_name)
def _log_input_output_decorator(func):
if not func_name:
message_prefix = f'{func.__name__}'
else:
message_prefix = f'{func_name}'
@wraps(func)
def wrapper(*args, **kwargs):
logger.info(f'{message_prefix} started')
if log_input_values:
if args:
logger.debug(f'{message_prefix} input args: {args}')
if kwargs:
logger.debug(f'{message_prefix} input kwargs: {kwargs}')
try:
result = func(*args, **kwargs)
except Exception as e:
logger.error(f'{message_prefix} error: {e}')
raise e
logger.info(f'{message_prefix} finished successfully')
logger.debug(f'{message_prefix} finished with result: {result}')
return result
return wrapper
return _log_input_output_decorator
上面的例子,现在看起来更加干净
@log_input_output(logger_name=__name__)
def generate_auth_token(self):
self.auth_token = uuid.uuid4()
self.save()
return str(self.auth_token)
但是日志较不干净
backend-api_1 | INFO: generate_auth_token started
backend-api_1 | DEBUG: generate_auth_token input args: (<self object at 0x7fc18085d1c8>)
backend-api_1 | INFO: generate_auth_token finished successfully
backend-api_1 | DEBUG: generate_auth_token finished with result: 66f4b515-c6f5-433c-885f-61c8a1f63ce5
在这种情况下,我们也缺少旧的令牌值。 我只是想知道,是否有适当的日志记录方式,因此代码看起来不会像混乱,但同时日志更易读?
答案 0 :(得分:1)
一种可能的解决方案是将__repr__
添加到您的类中,该类将返回您想要记录的内容:
def __repr__(self):
return str(self.auth_token)
该解决方案并不灵活,因为如果您想将self.auth_token
登录到一个功能中,而将self.other_attribute
登录到另一个功能中,则需要每次都登录它们:
def __repr__(self):
return str(self.auth_token, self.other_attribute)
因为您只想记录def generate_auth_token(self):
,它会弄乱self.auth_token
的日志
我可以建议的另一种解决方案是使用eval
。我知道eval
是邪恶的,但也许是使用它的合适位置。修改您的代码:
1)装饰器将按以下方式使用:
@log_input_output(logger_name=__name__, log_eval='f"token is {self.auth_token}"')
2)参数log_eval
:
def log_input_output(logger_name='', func_name=None, log_input_values=True, log_eval=''):
3)if log_eval
放在if kwargs
之后:
if kwargs:
logger.debug(f'{message_prefix} input kwargs: {kwargs}')
if log_eval:
signature = inspect.signature(func)
bind = signature.bind(*args, **kwargs)
custom_log = eval(log_eval, bind.arguments)
logger.debug(f'{message_prefix} custom log: {custom_log}')
因此,日志将如下所示:
generate_auth_token custom log: token is d0173388-f043-4e8b-8630-05634e2fd3c6
使用此解决方案,您只能在执行其他功能时记录self.other_attribute
:
@log_input_output(logger_name=__name__, log_eval='f"other_attribute is {self.other_attribute}"')
def other_function(self):
pass