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

2023-11-25

想象一下我有一个基于 Aiohttp 的 Web 应用程序:

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)

其输出是(例如):

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”类似)...


在“经典”非异步 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(使用前将#替换为@)

Aiohttp日志记录:如何区分不同请求的日志消息? 的相关文章

随机推荐

  • 动态更改 Angular 4 组件的模板

    使用 Angular 4 1 我尝试在渲染模块之前动态更改模块类型的模板 这可能吗 我们正在页面上引导未知数量的组件 来自已知的组件类型列表 并且该页面可能包含相同类型的多个组件 我找到了一种方法 为每个组件提供不同的选择器 以便可以单独呈
  • 保留顺序的 HashSet

    我需要一个保留插入顺序的 HashSet 框架中是否有任何实现 标准 NETHashSet不保留插入顺序 对于简单的测试 插入顺序可能会因意外而被保留 但不能保证并且并不总是这样 证明中间做一些删除就足够了 有关更多信息 请参阅此问题 Ha
  • 防止将查询生成器与 DB::raw() 相结合的查询的 SQL 注入

    在 Laravel 4 中 我想保护一些复杂的数据库查询免遭 SQL 注入 这些查询结合使用查询生成器和 DB raw 这是一个简化的示例 field email user DB table users gt select DB raw f
  • Android 持久空间:“无法弄清楚如何从光标读取此字段”

    我正在尝试使用新的 Android Persistence Room 库在两个数据库表之间创建关系 我查看了文档并尝试实现在以下位置找到的示例https developer android com reference android arc
  • 将 SVG 转换为 PDF

    如何以编程方式将 SVG 文件转换为 PDF 在生成 PDF 之前 我需要在某些方面更改 SVG 因此仅使用工具对其进行预转换是不够的 理想情况下使用 Java 但 Perl 或 PHP 也可以 显然 我基本上正在考虑 Apache FOP
  • 以编程方式启动德比

    请看下面的代码 数据库连接器 java import java sql import javax swing public class DataBaseConnector private Connection con public Data
  • 在 Visual Studio 2015 中混合 - 无设计视图

    我想在 Visual Studio 2015 中用 C 构建一个应用程序 并且想使用 Blend 创建一个用户界面 但它只向我显示 MainPage xaml 中的代码 而不是图形视图 我应该怎么办 它看起来是这样的 您必须将应用程序的目标
  • 在 R 中,以点开头的变量存储在哪里?

    我对以点开头的变量感到好奇 例如 var lt 100 它不属于全球环境 这个变量属于什么环境 gt ls all names TRUE envir GlobalEnv 1 Random seed var a 查看手册页ls 通过输入 ls
  • 无法在 vscode 上渲染 ipynb 文件中的图像

    我已经安装了 vscode 和官方的 microsoft python 包 除了 markdown 中的图像渲染之外 一切都很好 我尝试使用以下代码在降价单元格中显示图像 img src images grad summary png st
  • 对象不支持属性或方法“indexOf”

    我正在使用下面的代码 它是如
  • 无法加载文件或程序集“Microsoft.ReportViewer.Common,版本=11.0.0.0”

    我最近升级了我的 n 层解决方案 NET 3 5 与 2008 到 4 5 Visual Studio 2012 除了 Crystal Reports 之外 一切都很顺利 我必须通过以下链接为 Visual Studio 2012 安装新的
  • Pickle 转储替换当前文件数据

    当我使用pickle时 它工作得很好 我可以卸载任何负载 问题是 如果我关闭程序并尝试再次转储 它会用新的转储替换旧的文件数据 这是我的代码 import pickle import os import time dictionary di
  • 系统如何选择正确的页表?

    让我们关注单处理器计算机系统 据我所知 当创建进程时 就会建立页表 将虚拟地址映射到物理内存地址空间 每个进程都有自己的页表 存储在内核地址空间中 但是 由于不仅有一个进程在运行 并且会发生很多上下文切换 MMU 如何为进程选择正确的页表呢
  • 应用程序已部署。无需重新安装

    无法在模拟器上运行应用程序 也无法在设备本身上运行应用程序 当我在 Android 设备上运行它时 它说 应用程序已部署 无需重新安装 当我在手机上什至找不到项目应用程序时 如果您想确保应用程序已重新安装 您可以在命令行中执行此操作 adb
  • SpriteKit如何获取正确的屏幕尺寸

    我试过了 self frame size self view frame size UIScreen mainScreen bounds size 它们都不起作用 如何获得设备的正确屏幕尺寸 您可以使用以下快速代码来获取屏幕尺寸 let d
  • 有没有办法重写Spark RDD unique以使用mapPartitions而不是distinct?

    我有一个 RDD 太大 无法一致地执行不同的语句而不产生虚假错误 例如 SparkException 阶段失败 4 次 ExecutorLostFailure HDFS 文件系统关闭 达到执行器失败的最大数量 由于 SparkContext
  • OnGlobalLayoutListener:弃用和兼容性

    我必须使用OnGlobalLayoutListener对象 然后删除侦听器 我遇到了不推荐使用的方法的问题 我用以下代码解决了这个问题 protected void onCreate Bundle savedInstanceState fi
  • Android 中语音识别使用的比较:通过 Intent 还是线程内?

    介绍 Android 为我提供了两种使用语音识别的方式 The first方式是通过Intent 如这个问题 意图示例 A new Activity被推到堆栈的顶部 该堆栈聆听用户的声音 听到一些语音 尝试转录它 通常通过云 然后通过一个将
  • 在 Tensorflow-lite 中输入具有动态尺寸的图像

    我有一个张量流模型 它接受不同大小的输入图像 inputs layers Input shape 128 None 1 name x input
  • Aiohttp日志记录:如何区分不同请求的日志消息?

    想象一下我有一个基于 Aiohttp 的 Web 应用程序 from aiohttp import web import asyncio import logging logger logging getLogger name async
Powered by Hwhale