【问题标题】:Can I access the request object in a console error?我可以在控制台错误中访问请求对象吗?
【发布时间】:2021-05-28 10:12:27
【问题描述】:

我对 Django 不是很熟悉,但必须对项目的日志记录方式进行一些更改。我想将请求对象中的信息添加到日志中。

import logging

MODULE_LOGGER = logging.getLogger(__name__)

class IndexPage():

  def get(self, request, *args, **kwargs):
    
    MODULE_LOGGER.warning("log me!")
    # code...
    return self.render_to_response(context)

然后这会被这个类注销:

import json
from fluent import handler

class JsonFormatter(handler.FluentRecordFormatter):
    def format(self, record):
        data = super(JsonFormatter, self).format(record)
        return json.dumps(data)

我们有以下记录器设置(我不太明白)

LOGGING = {
    "version": 1,
    "disable_existing_loggers": True,
    "root": {"level": "WARNING", "handlers": ["sentry", "json_console"]},
    "formatters": {
        "console": {"datefmt": syslog_date_fmt, "format": syslog_msg_format},
        "json_fmt": {
            "()": "out_project.logs.JsonFormatter",
            "format": {
                "level": "%(levelname)s",
                "hostname": "%(hostname)s",
                "name": "%(name)s",
                "where": "%(module)s.%(funcName)s",
                "stack_trace": "%(exc_text)s",
            },
        },
    },
    "filters": {
        "require_debug_false": {"()": "django.utils.log.RequireDebugFalse"},
        "file_logging": {"": ""},
    },
    "handlers": {
        "null": {
            "level": "DEBUG",
            "class": "logging.NullHandler",
        },
        "console": {
            "level": "DEBUG",
            "class": "logging.StreamHandler",
            "formatter": "console",
        },
        "sentry": {
            "level": "WARNING",
            "class": "raven.contrib.django.raven_compat.handlers.SentryHandler",
        },
        "json_console": {
            "level": "INFO",
            "class": "logging.StreamHandler",
            "formatter": "json_fmt",
        },
    },
    "loggers": {
        "raven": {
            "level": "INFO",
            "handlers": ["json_console"],
            "propagate": False,
        },
        "sentry.errors": {
            "level": "INFO",
            "handlers": ["json_console"],
            "propagate": False,
        },
        "out_project": {
            "level": "INFO",
            "propagate": True,
        },
    },
}

Django 文档讨论了 django.request 记录器,但这似乎是在请求出现问题时(比如 4XX、5XX 错误)。我尝试添加这个记录器,它的处理程序是json_console 处理程序,但是request 对象没有出现在record 对象上。

理想情况下,我希望将请求对象中的数据添加到记录中,或者让它自动放在那里。我假设一旦它存在,我就可以通过更新 json_fmt.format 属性来更改日志的外观。

据我了解,我可以将其他信息添加到各个日志中,但是我希望从请求中获取的数据需要添加到通过来自logging.getLogger(__name__) 的记录器完成的每个日志中。

我对 Django 中的日志记录工作方式非常陌生,因此,如果我解释或理解有误,我深表歉意。

【问题讨论】:

    标签: python django logging


    【解决方案1】:

    如果您想调试错误,请使用breakpoint(),这样您将获得一个交互式控制台,您可以在其中输入变量并查看其值。

    如果您想记录请求对象,请像这样修改您的JsonFormatter

    class JsonFormatter(handler.FluentRecordFormatter):
    
        def format(self, record):
    
            if type(record) in [str]:
                data = super(JsonFormatter, self).format(record)
    
            else:
                # request object | manually serialize it
                request_data = f"{request.status} | {request.user.username} | {request.data} | ... other request data"
                data = super(JsonFormatter, self).format(request_data)
    
            return json.dumps(data)
    

    现在在你的views.py中为IndexPage

    class IndexPage():
    
        def get(self, request, *args, **kwargs):
        
            MODULE_LOGGER.warning(request)
            # code...
            return self.render_to_response(context)
    

    【讨论】:

    • 有没有办法让请求对象自动提供给记录器?这可以通过让另一个层来处理日志功能来解决吗?
    • 是的,你可以编写一个 Mixin 类来做同样的事情......并在视图层中继承它
    • 你介意分享一个这个 mixin 的例子吗?
    【解决方案2】:

    如果您想了解更多关于 logger 的信息,您需要查看 Python 文档,它与 Django 无关。 Django 使用 JSON 来配置 Python 记录器。见https://docs.python.org/3/howto/logging.html

    要在每个日志中都有请求,您可以采用显式方式,即全部替换

    MODULE_LOGGER.warning("log me!")
    

    MODULE_LOGGER.warning("log me!", extra={"request": request})
    

    然后在您的格式化程序中,您将拥有请求对象

    class JsonFormatter(handler.FluentRecordFormatter):
        def format(self, record):
            print(record.request) # Probably you want to do something more useful here.
            data = super(JsonFormatter, self).format(record)
            return json.dumps(data)
    

    如果这不适合您并且您想自动和隐式将请求传递给您的记录器,那么您需要做一些更复杂的事情,比如中间件。大多数时候 Django 是使用基于线程/进程的服务器部署的,因此当前线程一次处理一个请求。因此我们可以将当前请求对象存储在线程本地存储中(使用中间件),然后在我们的记录器中获取它。

    添加这个,让我们在文件“my_pretty_middleware.py”中说

    import threading
    
    _local = threading.local()
    _local.current_request = None
    
    
    class StoreRequestMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
    
        def __call__(self, request):
            try:
                _local.current_request = request
                return self.get_response(request)
            finally:
                _local.current_request = None
    
    
    def get_current_request():
        return _local.current_request
    

    然后将此中间件添加到您的中间件之上。喜欢

    MIDDLEWARE = [
        "my_pretty_middleware.StoreRequestMiddleware"
        "django.middleware.security.SecurityMiddleware",
        "django.middleware.common.CommonMiddleware",
        ...
    ]
    

    然后在您的格式化程序(或代码的任何部分)中,您可以获得请求对象。

    from my_pretty_middleware import get_current_request
    
    
    class JsonFormatter(handler.FluentRecordFormatter):
        def format(self, record):
            request = get_current_request()
            if request:  # Beware it can be None in case you're logging outside of request cycle.
                print(request)  # Probably you'd like to do something else
            data = super(JsonFormatter, self).format(record)
            return json.dumps(data)
    

    【讨论】:

      【解决方案3】:

      检查并安装 requestLogging。

      它可以帮助您使用请求对象修改和格式化日志。

      【讨论】:

        【解决方案4】:

        我最终使用了 pip 包django-middleware-global-request。我可以在任何地方导入它并使用请求对象的方法get_request

        from django_middleware_global_request.middleware import get_request
        
        
        class JsonFormatter(handler.FluentRecordFormatter):
        
          def format(self, record):
            def add_request_data(data):
            request = get_request()
            if request:
              # do stuff with request and data
            return data
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-15
          • 2018-10-21
          • 1970-01-01
          • 1970-01-01
          • 2017-10-03
          • 1970-01-01
          相关资源
          最近更新 更多