correct/common 记录异步调用的方法
A correct/common way to log async calls
假设我们有一些 IO 操作的异步调用,我们想要记录它。
最简单的方法如下所示:
async def f():
logger.log('io was called')
await call_some_io()
但是当我们 运行 log()
函数切换上下文并记录其他内容并且仅在此执行之后 call_some_io()
.
时,我们显然会遇到这种情况
下一个方法看起来更可靠:
async def f():
await call_some_io()
logger.log('io was called')
我们正在等待 call_some_io() 并在它之后记录它。看起来在这种情况下我们有一致的调用。
但是还有第三种使用上下文管理器的方法:
async def f():
with LoggingContext:
await call_some_io()
这里的 LoggingContext
是一些 ContextManager,其中 __exit__
方法有一些日志记录调用。
所以问题是:哪种记录异步调用的方法最常见且最可靠?
所有方法都是稳健的。上下文管理器可能不那么常见,但仍然很强大。
但是,示例 #1 和 #2 具有不同的语义。第一条日志消息应显示为 about to call io
而不是 io was called
,因为尚未发出调用。
LoggingContext
示例对开发人员来说相当方便,但在执行顺序方面等同于示例 #2。
在启动另一个协程的协程中使用日志记录是相对可靠的,因为正如您已经注意到的那样,可以点击上下文切换。实际上只有两种情况:等待之前和之后以及几种可能的方法:登录协程、日志装饰器和日志上下文管理器。我相信这不是一个详尽的清单。
在 io 启动之前在协程中记录日志
async def coro():
logger.log('Before io start')
await call_some_io()
在 io 开始之前记录的装饰器
def logger(f):
def warp(*arguments, **kwarguments):
logging.log("Before io start")
f(*arguments. **kwarguments)
return wrap
@logger
async def coro():
# Async logic...
在 io 开始之前记录的日志上下文管理器
class Logger(object):
def __enter__(self):
logging.log("Before io start")
# Empty __exit__ ...
with Logger():
await coro()
在 io 调用后记录的装饰器的一个更棘手的例子。
def logger(f):
async def wrapper():
await f()
logging.log("After io start")
return wrapper
装饰器的包装器也必须是协程才能在 io 启动后记录异步调用。而且使用异步记录器记录异步调用绝对不可靠;)
In-coro 和上下文管理器方法很容易在调用后转换为日志记录,因此我将跳过示例。
所有这些示例都同样常见,因为 Python 非常具有艺术性,并且没有像 Java 那样对编程模式的投入。所以所有的方法对我来说都是 Pythonic 方式。
假设我们有一些 IO 操作的异步调用,我们想要记录它。 最简单的方法如下所示:
async def f():
logger.log('io was called')
await call_some_io()
但是当我们 运行 log()
函数切换上下文并记录其他内容并且仅在此执行之后 call_some_io()
.
下一个方法看起来更可靠:
async def f():
await call_some_io()
logger.log('io was called')
我们正在等待 call_some_io() 并在它之后记录它。看起来在这种情况下我们有一致的调用。
但是还有第三种使用上下文管理器的方法:
async def f():
with LoggingContext:
await call_some_io()
这里的 LoggingContext
是一些 ContextManager,其中 __exit__
方法有一些日志记录调用。
所以问题是:哪种记录异步调用的方法最常见且最可靠?
所有方法都是稳健的。上下文管理器可能不那么常见,但仍然很强大。
但是,示例 #1 和 #2 具有不同的语义。第一条日志消息应显示为 about to call io
而不是 io was called
,因为尚未发出调用。
LoggingContext
示例对开发人员来说相当方便,但在执行顺序方面等同于示例 #2。
在启动另一个协程的协程中使用日志记录是相对可靠的,因为正如您已经注意到的那样,可以点击上下文切换。实际上只有两种情况:等待之前和之后以及几种可能的方法:登录协程、日志装饰器和日志上下文管理器。我相信这不是一个详尽的清单。
在 io 启动之前在协程中记录日志
async def coro():
logger.log('Before io start')
await call_some_io()
在 io 开始之前记录的装饰器
def logger(f):
def warp(*arguments, **kwarguments):
logging.log("Before io start")
f(*arguments. **kwarguments)
return wrap
@logger
async def coro():
# Async logic...
在 io 开始之前记录的日志上下文管理器
class Logger(object):
def __enter__(self):
logging.log("Before io start")
# Empty __exit__ ...
with Logger():
await coro()
在 io 调用后记录的装饰器的一个更棘手的例子。
def logger(f):
async def wrapper():
await f()
logging.log("After io start")
return wrapper
装饰器的包装器也必须是协程才能在 io 启动后记录异步调用。而且使用异步记录器记录异步调用绝对不可靠;)
In-coro 和上下文管理器方法很容易在调用后转换为日志记录,因此我将跳过示例。
所有这些示例都同样常见,因为 Python 非常具有艺术性,并且没有像 Java 那样对编程模式的投入。所以所有的方法对我来说都是 Pythonic 方式。