A better way to logging in Python
python的通用日志装饰器
任何生产应用程序都可能有一些关于如何以及需要在您的应用程序中记录什么的指南。 更多时候,这些指南源于常见的行业模式,例如“记录所有异常”。 然而,实施这些准则留给了个人开发人员,并导致在整个代码库中重复相同的日志记录语句集。 例如,要记录所有异常,您将在每个 except
块中都有一个记录语句,用于捕获异常并将其记录在 ERROR
级别下。 但是,由于开发人员各自的开发风格,相同场景的日志记录语句可能会有所不同。 超时会导致应用程序中的日志记录碎片化和不一致。 此外,开发人员可能会犯错误并错过在必要的地方有日志记录。
缓解此问题的一种方法是利用 Python 的装饰器功能。 本文将简要概述装饰器,并演示如何创建装饰器来抽象这些常见的日志记录语句。 你可以阅读更多关于装饰器和它们可以在这个优秀的Primer on Python Decorators.
What are decorators
装饰器是一个函数,它接受另一个函数并扩展它的行为而不显式修改它。 这些也被称为 higher-order functions.
Python的函数是 first-class citizens. 这意味着函数可以作为参数传递,也可以作为赋值的对象。 因此,如果您有一个函数 def sum(a, b=10)
,您可以将它用作任何其他对象并深入了解它的属性。
def sum(a, b=10):
return a+b
>>> sum
<function sum at 0x7f35e9dde310>
>>> sum.__code__.co_varnames # Names of local variables
('a', 'b')
由于函数的行为类似于对象,因此您可以将“sum”分配给另一个函数。 然后调用 sum
将调用另一个函数,而不是我们之前定义的函数。 装饰器通过为 sum
分配一个新函数来利用这种行为,该函数以 sum
作为参数并围绕它包装一些额外的逻辑,从而在不修改函数本身的情况下 扩展 它。
def my_decorator(func):
def wrapper(*args, **kwargs):
# do something before `sum`
result = func(*args, **kwargs)
# do something after `sum`
return result
return wrapper
sum = my_decorator(sum)
>>> sum
<function my_decorator.<locals>.wrapper at 0x7f9c0359b0d0>
这种模式非常普遍,以至于 Python 有一个语法糖来装饰一个函数。 因此,我们可以在 sum
方法之上使用 @
符号,而不是 sum = my_decorator(sum)
,如下所示 -
@my_decorator # Equivalent to `sum = my_decorator(sum)` after the method
def sum(a, b=10):
return a+b
Logging Decorator
我们将创建一个装饰器来处理两种常见的日志记录场景 - 将异常记录为 ERROR 并将方法参数记录为 DEBUG 日志。
让我们首先捕获异常并使用 python logging
库记录它。
import functools
import logging
logging.basicConfig(level = logging.DEBUG)
logger = logging.getLogger()
def log(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
try:
result = func(*args, **kwargs)
return result
except Exception as e:
logger.exception(f"Exception raised in {
func.__name__}. exception: {
str(e)}")
raise e
return wrapper
@log
def foo():
raise Exception("Something went wrong")
ERROR:root:Exception raised in