假设我们有一些IO操作的异步调用,我们想记录它。 最简单的方法如下:
$ arm-none-linux-gnueabi-gcc -v
Using built-in specs.
COLLECT_GCC=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/bin/arm-none-linux-gnueabi/arm-none-linux-gnueabi-gcc
COLLECT_LTO_WRAPPER=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/libexec/arm-none-linux-gnueabi/gcc/arm-none-linux-gnueabi/4.8.4/lto-wrapper
Target: arm-none-linux-gnueabi
Configured with: /home/alima/projects/yocto/build/tmp-tiny/work-shared/gcc-4.8.4-r0/gcc-4.8.4/configure --build=x86_64-linux --host=x86_64-linux --target=arm-none-linux-gnueabi --prefix=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr --exec_prefix=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr --bindir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/bin/arm-none-linux-gnueabi --sbindir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/bin/arm-none-linux-gnueabi --libexecdir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/libexec/arm-none-linux-gnueabi --datadir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/share --sysconfdir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/etc --sharedstatedir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/com --localstatedir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/var --libdir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/lib/arm-none-linux-gnueabi --includedir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/include --oldincludedir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/include --infodir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/share/info --mandir=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr/share/man --disable-silent-rules --disable-dependency-tracking --with-libtool-sysroot=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux --enable-clocale=generic --with-gnu-ld --enable-shared --enable-languages=c,c++ --enable-threads=posix --disable-multilib --enable-c99 --enable-long-long --enable-symvers=gnu --enable-libstdcxx-pch --program-prefix=arm-none-linux-gnueabi- --without-local-prefix --enable-target-optspace --enable-lto --enable-libssp --disable-bootstrap --disable-libmudflap --with-system-zlib --with-linker-hash-style=gnu --enable-linker-build-id --with-ppl=no --with-cloog=no --enable-checking=release --enable-cheaders=c_global --with-gxx-include-dir=/not/exist/usr/include/c++/4.8.4 --with-sysroot=/not/exist --with-build-sysroot=/home/alima/projects/yocto/build/tmp-tiny/sysroots/board --enable-poison-system-directories --with-mpfr=/home/alima/projects/yocto/build/tmp-tiny/sysroots/x86_64-linux/usr --with-system-zlib --disable-nls --with-arch=armv7-a
Thread model: posix
gcc version 4.8.4 (GCC)
但是当我们运行async def f():
logger.log('io was called')
await call_some_io()
函数切换上下文并记录其他内容并且仅在执行log()
之后,我们显然可以遇到这种情况。
下一步方法看起来更健壮:
call_some_io()
我们正在等待call_some_io(),之后我们正在记录它。看起来在这种情况下我们有一致的电话。
但是有第三种使用上下文管理器的方法:
async def f():
await call_some_io()
logger.log('io was called')
这里的async def f():
with LoggingContext:
await call_some_io()
是一些ContextManager,其中LoggingContext
方法有一些记录调用。
所以问题是:哪种记录异步调用的方法最常见且最健壮?
答案 0 :(得分:2)
所有方法都很健全。上下文管理器可能不那么常见,但仍然很强大。
但是,示例#1和#2具有不同的语义。第一条日志消息应显示为about to call io
而不是io was called
,因为尚未发出任何通话。
LoggingContext
示例对于开发人员来说相当方便,但在执行顺序方面等于示例#2。
答案 1 :(得分:0)
在协程中使用登录启动另一个协程是相对健壮的,因为您已经注意到可以触发上下文切换。实际上只有两种情况:等待之前和之后以及几种可能的方法:登录协程,记录装饰器和记录上下文管理器。我相信这不是一份详尽的清单。
在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对我来说。