使用Django + Gunicorn应用程序在stdout上打印用户名

xxe27gdn  于 2022-11-26  发布在  Go
关注(0)|答案(2)|浏览(140)

bounty将在6天后过期。回答此问题可获得+200声望奖励。Cospel正在寻找规范答案

现在我的Django + Gunicorn应用程序只打印以下信息:

[03.10.2022 19:43:14] INFO [django.request:middleware] GET /analyse/v2/ping - 200

如果请求获得授权,我还想在状态代码后面显示用户(用户名/电子邮件),类似于:

[03.10.2022 19:43:14] INFO [django.request:middleware] GET /analyse/v2/ping - 200 - useremail@outlook.com

如果请求未授权,则填写“未授权”:

[03.10.2022 19:43:14] INFO [django.request:middleware] GET /analyse/v2/ping - 200 - UNAUTHORIZED

我如何才能通过Django和Gunicorn的组合实现这一点?
谢谢你

gcuhipw9

gcuhipw91#

一个定制的中间件是你可以轻松实现这一点的方法。你可以做如下的事情。

import logging

from django.utils import timezone

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
sh = logging.StreamHandler()
logger.addHandler(sh)

class LogRequest:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)

        user = (
            request.user.email
            if request.user.is_authenticated
            else "UNAUTHORIZED"
        )

        logger.info(
            f"[{timezone.now().strftime('%d.%m.%Y %H:%M:%S')}] INFO [myapp.custom_logger] {request.method} {request.path} - {response.status_code} - {user}"
        )

        return response

然后,您可以通过注册来配置并激活新的中间件。

MIDDLEWARE = [
    "django.middleware.gzip.GZipMiddleware",
    "django.middleware.security.SecurityMiddleware",
    ...
    "myapp.middleware.LogRequest",
]

这将生成如下所示的输出。

[25.11.2022 15:57:37] INFO [myapp.custom_logger] GET /analyse/v2/ping - 200 - oppen@heimer.xyz
u4dcyp6a

u4dcyp6a2#

不清楚这行日志来自哪里。据我所知,Django只记录4xx和5xx对django.request logger的请求。这看起来也不像gunicorn访问日志行。如果你用自己的代码初始化这行日志,你应该可以很容易地添加用户。
这里有几个通用的解决方案。

(选项1)对于gunicorn访问日志行

您没有访问Django的请求对象的权限,因此无法从gunicorn中检索用户,不过,您可以通过在响应头中添加用户来解决这个问题。

  • 您的应用程序/middleware.py *
class UserHeaderMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        user = request.user
        response['X-User'] = user.email if user.is_authenticated() else 'UNAUTHORIZED'
        return response
  • 您的项目/settings.py *
MIDDLEWARE = [
    ...,
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    ...,  # Your custom middleware must be called after authentication
    'yourapp.middleware.UserHeaderMiddleware',
    ...,
]

然后更改gunicorn的access_log_format设置以包含此头。'%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%({x-user}o)s"'

(选项2)用于django.request记录器

如果您的日志行被发送到django.request记录器,则它有可能在额外的上下文中提供请求。
在这种情况下,您可以编写一个自定义格式化程序来包含用户:

  • 您的应用程序/logging.py *
from logging import Formatter

class RequestFormatter(Formatter):

    def format(self, record):
        request = getattr(record, 'request', None)
        if user := getattr(request, 'user', None):
            record.user = user.email if user.is_authenticated() else 'UNAUTHORIZED'
        else:
            record.user = '-'
        return super().format(record)
  • 您的应用程序/logging.py *
LOGGING = {
    ...,
    'formatters': {
        ...,
        "django.request": {
            "()": "yourapp.logging.RequestFormatter",
            "format": "[{asctime}] {levelname} [{name}] {message} - {status_code} - {user}",
            "style": "{",
        },
    },
    'loggers': {
        ...,
        "django.request": {
            "handlers": ...,
            "level": "INFO",
            "formatter": 'django.request',
       }
       ...,
    },
}

(选项3)告诉Django在django.request中记录所有请求

Django只在django.request中记录4xx和5xx请求。
但是我们可以通过使用自定义WSGI处理程序来更改此行为。
在 *yourproject/wsgi.py * 中,您应该有类似以下的内容:

import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yourproject.settings')
application = get_wsgi_application()

您可以将其更改为使用自定义WSGI处理程序:

import os
import django
from django.core.wsgi import WSGIHandler
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'yourproject.settings')

class CustomWSGIHandler(WSGIHandler):
    def get_response(self, request):
        # Note that this is only a copy of BaseHandler.get_response()
        # without the condition on log_response()
        set_urlconf(settings.ROOT_URLCONF)
        response = self._middleware_chain(request)
        response._resource_closers.append(request.close)
        log_response(
            "%s: %s",
            response.reason_phrase,
            request.path,
            response=response,
            request=request,
        )
        return response

django.setup(set_prefix=False)
application = CustomWSGIHandler()

然后,参考选项2将用户包括在格式化程序中。

(选项4)创建中间件以添加新的日志行

如果没有访问此日志行的权限来更新它,并且没有访问日志格式化程序中的请求的权限,则必须手动添加新的日志行(并且可能将第一行静默以避免重复)。

  • 您的应用程序/middleware.py *
import logging

logger = logging.getLogger('django.request')

class LoggingMiddleware:

    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        response = self.get_response(request)
        user_email = request.user.email if request.user.is_authenticated() else 'UNAUTHORIZED'
        logger.info(f"{request.method} {request.path} - {request.status_code} - {user_email}")
        return response
  • 您的项目/settings.py *
MIDDLEWARE = [
    ...,
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    ...,  # Your custom middleware must be called after authentication
    'yourapp.middleware.LoggingMiddleware',
    ...,
]

相关问题