首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Aiohttp日志记录:如何区分不同请求的日志消息?

Aiohttp日志记录:如何区分不同请求的日志消息?
EN

Stack Overflow用户
提问于 2019-11-11 12:51:10
回答 1查看 1.8K关注 0票数 5

假设我有一个基于Aiohttp的web应用程序:

代码语言:javascript
复制
from aiohttp import web
import asyncio
import logging

logger = logging.getLogger(__name__)

async def hello(request):
    logger.info('Started processing request')
    await asyncio.sleep(1)
    logger.info('Doing something')
    await asyncio.sleep(1)
    return web.Response(text="Hello, world!\n")

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s %(name)-14s %(levelname)s: %(message)s')

app = web.Application()
app.add_routes([web.get('/', hello)])
web.run_app(app)

它的输出是(例如):

代码语言:javascript
复制
2019-11-11 13:37:14,757 __main__       INFO: Started processing request
2019-11-11 13:37:14,757 __main__       INFO: Started processing request
2019-11-11 13:37:15,761 __main__       INFO: Doing something
2019-11-11 13:37:15,761 __main__       INFO: Doing something
2019-11-11 13:37:16,765 aiohttp.access INFO: 127.0.0.1 [11/Nov/2019:12:37:14 +0000] "GET / HTTP/1.1" 200 165 "-" "curl/7.66.0"
2019-11-11 13:37:16,768 aiohttp.access INFO: 127.0.0.1 [11/Nov/2019:12:37:14 +0000] "GET / HTTP/1.1" 200 165 "-" "curl/7.66.0"

我如何知道哪些日志消息属于什么请求?

我想在每条日志消息中看到一些“请求id”(类似于微服务中的“相关id”).

EN

回答 1

Stack Overflow用户

发布于 2019-11-11 12:51:10

在“经典”的非异步web应用程序中,它很简单--一个进程(或线程)一次只处理一个请求,所以您只记录进程/线程id (日志格式:%(process)d %(thread)d)。

在异步(异步)程序中,单个线程中的事件循环中通常运行多个不同的东西(在web应用程序中:处理不同的请求),因此仅记录进程/线程id是不够的。您需要识别的不是操作系统线程,而是相关异步任务的“线程”--这就是ContextVar的作用所在。

步骤1:创建上下文

代码语言:javascript
复制
request_id = ContextVar('request_id')

步骤2:为每个请求设置这个上下文值

代码语言:javascript
复制
@web.middleware
async def add_request_id_middleware(request, handler):
    '''
    Aiohttp middleware that sets request_id contextvar and request['request_id']
    to some random value identifying the given request.
    '''
    req_id = secrets.token_urlsafe(5).replace('_', 'x').replace('-', 'X')
    request['request_id'] = req_id
    token = request_id.set(req_id)
    try:
            return await handler(request)
    finally:
        request_id.reset(token)

app = web.Application(middlewares=[add_request_id_middleware])

步骤3:在每个日志消息中自动插入这个上下文值

代码语言:javascript
复制
def setup_log_record_factory():
    '''
    Wrap logging request factory so that [{request_id}] is prepended to each message
    '''
    old_factory = logging.getLogRecordFactory()

    def new_factory(*args, **kwargs):
        record = old_factory(*args, **kwargs)
        req_id = request_id.get(None)
        if req_id:
            record.msg = f'[{req_id}] {record.msg}'
        return record

    logging.setLogRecordFactory(new_factory)

setup_log_record_factory()

步骤4:由于aiohttp请求访问日志消息记录在我们设置上下文var的作用域之外,我们需要定义自己的AccessLogger来修复这个问题:

代码语言:javascript
复制
from aiohttp.web_log import AccessLogger

class CustomAccessLogger (AccessLogger):

    def log(self, request, response, time):
        token = request_id.set(request['request_id'])
        try:
            super().log(request, response, time)
        finally:
            request_id.reset(token)

web.run_app(app, access_log_class=CustomAccessLogger)

已完成的示例输出:

代码语言:javascript
复制
2019-11-11 13:49:34,167 __main__       INFO: [cNniXu8] Started processing request
2019-11-11 13:49:34,168 __main__       INFO: [oWzMYds] Started processing request
2019-11-11 13:49:35,169 __main__       INFO: [cNniXu8] Doing something
2019-11-11 13:49:35,169 __main__       INFO: [oWzMYds] Doing something
2019-11-11 13:49:36,172 aiohttp.access INFO: [cNniXu8] 127.0.0.1 [11/Nov/2019:12:49:34 +0000] "GET / HTTP/1.1" 200 165 "-" "curl/7.66.0"
2019-11-11 13:49:36,174 aiohttp.access INFO: [oWzMYds] 127.0.0.1 [11/Nov/2019:12:49:34 +0000] "GET / HTTP/1.1" 200 165 "-" "curl/7.66.0"

完整的源代码在这里:gist.github.com/messa/c538fc267550ec67a1fed244183dcf1e

更新:--我已经为此创建了一个库:) Github.com/messa/aiohttp-请求-id-日志记录

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58801739

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档