记录异步调用的正确/常用方法

时间:2016-11-29 14:23:53

标签: python asynchronous python-3.5 python-asyncio

假设我们有一些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方法有一些记录调用。

所以问题是:哪种记录异步调用的方法最常见且最健壮?

2 个答案:

答案 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对我来说。