在“经典”非异步 Web 应用程序中,这很简单 - 一个进程(或线程)一次仅处理一个请求,因此您只需记录进程/线程 ID(日志记录格式:%(process)d %(thread)d
).
在异步 (asyncio) 程序中,通常有多个不同的事物在单个线程的事件循环中运行(在 Web 应用程序中:正在处理不同的请求),因此记录进程/线程 ID 是不够的。您需要以某种方式识别不是操作系统线程,而是相关异步任务的“线程” - 这就是上下文变量 is for.
第 1 步:创建上下文变量
request_id = ContextVar('request_id')
第 2 步:为每个请求设置此 contextvar 值
@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:自动将此 contextvar 值插入到每个日志消息中
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 请求访问日志消息记录在我们设置上下文变量的范围之外,因此我们需要定义自己的 AccessLogger 来解决此问题:
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)
完成
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)