快速实现一个Django日志中间件

什么是Django中间件

在开始之前我们先来看看什么是Middleware中间件:Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。Django中间件是注入在Django请求/响应处理流程中的钩子框架、能对request/response做处理。Django中间件有广泛的使用场景:比如登录认证、安全拦截、日志记录、性能上报、缓存处理、监控告警等场景。

image-20210115173828562

Django中间件有两种定义方式:使用函数和使用类来定义实现。下面我们先开一起看看使用函数如何实现一个基础的Django中间件、代码如下:

def simple_middleware(get_response):
    # 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。

    def middleware(request):
        # 此处编写的代码会在每个请求处理视图前被调用。

        response = get_response(request)
        # 此处编写的代码会在每个请求处理视图之后被调用。

        return response

    return middleware 

注:这里我们接受一个get_response的参数、get_response也是一个函数;他可以是Django里面用来处理视图的函数、处理视图的响应或者是另外一个Django中间件的处理函数;处理完成以后我们通过get_response来得到返回的结果。get_response其实返回了一个函数、里面的这个response其实是我们定义的middleware,这个middleware对请求的request进行处理、处理完成以后返回出去。

当然、除了用函数的方法来实现、我们也可以用类的方法来实现。Django提供的get_response方法,可以是一个真实的视图,也可能是请求处理链中的下一个中间件。使用类实现代码如下:

class SimpleMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.一次性设置和初始化

    def __call__(self, request):
        # Code to be executed for each request before
        # the view (and later middleware) are called.
        # 视图函数执行前的代码
        response = self.get_response(request)
        # Code to be executed for each request/response after
        # the view is called. 视图函数执行后的代码
        return response

注:这里首先我们需要定义一个__init__函数,然后我们再定义一个__call__函数;这个call函数其实和上面处理response的过程类似。但是不管是使用函数的方法也还还是使用类的方法也好、我们在处理的过程中既有request请求、又有最后返回的response对象。在这个过程中我们可以对request和response进行处理、比如我们把请求的参数记录下来、或者把开始处理的响应时间和结束时间都记录下来、然后我们就可以统计整个响应的耗时。当然、如果我们想对异常进行记录、我们也可以对异常做一个日志记录。

Django默认中间件

Django默认启用了很多中间件,比如有用来做安全事件拦截处理的安全中间件、也有对用户的登录和退出做处理的Sessions中间件等等:

MIDDLEWARE = [
    # 安全中间件、针对安全事件做拦截处理
    'django.middleware.security.SecurityMiddleware',
    # Session中间件、对用户的登录和退出做处理
    'django.contrib.sessions.middleware.SessionMiddleware',
    # 公共Common中间件、常用的中间件都放在CommonMiddleware中
    'django.middleware.common.CommonMiddleware',
    # CSRF中间件、一般用来做跨站攻击处理
    'django.middleware.csrf.CsrfViewMiddleware',
    # 用户认证登录中间件
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    # 消息处理、对用户的操作做处理
    'django.contrib.messages.middleware.MessageMiddleware',
    # 点击处理
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

自定义Django中间件

下面我们来自定义实现一个Django中间件:def performance_logger_middleware(get_response),这个Django中间件用来记录用户访问的URL、URL传递的参数以及处理过程中耗费的时间等。这里我们用单独的文件来记录性能日志,这样我们可以对这些数据做进一步的分析;当然也可以把系统访问日志流转到其他的系统里面来做进一步的处理。我们使用函数来实现、具体代码如下:

# 在某一个APP下面创建一个perfomance.py文件、并引入下面的内容。
import time
import logging

logger = logging.getLogger(__name__)


def perfomance_logger_middleware(get_response):
    def middleware(request):
        # 记录开始时间
        start_time = time.time()
        response = get_response(request)
        # 计算耗时时间
        duration = time.time() - start_time

        response["X-Page-Duration-ms"] = int(duration * 1000)
        logger.info("%s %s %s", duration, request.path, request.GET.dict())
        return response

    return middleware

注:这里我们返回一个middleware函数、这个函数接受一个叫做get_response的参数;里面 def middleware(request) 函数的参数是 request 、所以这个函数里面我们用get_response对接收到的请求进行处理;处理完成之后把response进行返回。

上面的函数定义完成之后、我们就可以在settings.py的中间件配置里面把他配置进来:

MIDDLEWARE = [
    # 自定义的间件
    'chpa_data.middleware.performance_logger_middleware',
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

MIDDLEWARE里面有很多中间件、这里我们要记录整个请求的耗时;所以按照MIDDLEWARE的洋葱逻辑、我们需要把自定义的中间件放在第一个位置上;这样MIDDLEWARE会依次执行下面的中间件、所有的请求耗时都能够被我们统计到。如果我们把自定义的中间件放在最下面、那么上面的中间件耗时就不都不在我们的统计范围内了。

这里我们想单独记录请求的耗时、我们还需要去LOGGING里面做一个配置、具体如下:

LOGGING = {
    # 日志版本
    'version': 1,
    # 是否禁用已有的其他logger
    'disable_existing_loggers': False,
    # 这里我们定义一个simple格式的formatters
    'formatters': {
        'simple': {  # exact format is not important, this is the minimum information
            # asctime代表打印当前的时间、name代表当前的类、lineno代表类的行数、levelname代表日志等级、message就是日志信息
            'format': '%(asctime)s %(name)-12s %(lineno)d %(levelname)-8s %(message)s',
        },
    },
    'handlers': {
        # 控制台输出
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'simple',
        },
        # 邮件输出:错误级别的日志发送到邮件
        'mail_admins': {  # Add Handler for mail_admins for `warning` and above
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        },
        # 文本文件输出到指定文件
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'formatter': 'simple',
            'filename': os.path.join(LOG_DIR, 'admin.info.log'),
        },
        # 这里我们定义一个handlers的performance,把请求耗时写到文本文件里面去
        'performance': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'formatter': 'simple',
            'filename': os.path.join(LOG_DIR, 'performance.info.log'),
        },
    },
    # 系统全局级别默认的日志记录器、他是logger里面的一种特殊的记录器
    'root': {
        # 系统全局默认日志往控制台和文本文件同事输出
        'handlers': ['console', 'file'],
        'level': 'INFO',
    },
    # 关联handlers的performance
    'loggers': {
        'chpa_data.middleware': {
            'handlers': ['console', 'performance'],
            'level': 'INFO',
            'propagate': False,
        },
    },
}

然后我们启动项目去访问页面、我们可以在日志文件里面看到刚才访问的页面、以及访问请求的耗时等信息。

image-20210118155433866

在上面我们自定义的Django中间件中、我们通过response["X-Page-Duration-ms"] = int(duration * 1000)把请求耗时一起输入到了客户端页面、我们也可以到前端浏览器的调试控制台看到具体信息、如下:

image-20210118160025786

这样我们的自定义Django中间件就完成了、其实Django也为我们提供了一个已经写好的中间件类:MiddlewareMixin、需要使用的时候我们直接去继承即可。MiddlewareMixin 总共提供了四个定义方法:process_request(self,request);process_view(self, request, view_func, view_args, view_kwargs);process_exception(self, request, exception)和process_response(self, request, response)。

MiddlewareMixin的使用方法也很简单、我们在上面自定义的middleware.py里面导入MiddlewareMixin的包、代码如下:

from django.utils.deprecation import MiddlewareMixin

from django.shortcuts import render, HttpResponse

class Middleware(MiddlewareMixin):
    def process_request(self, request):
       print("md1  process_request 方法。", id(request)) #在视图之前执行

然后我们就可以在settings.py里面去引用啦、关于MiddlewareMixin的更多内容、感兴趣的小伙伴可以去官方文档查看详细信息:https://docs.djangoproject.com/en/2.1/_modules/django/middleware/common/#CommonMiddleware

推荐文章

3条评论

  1. I was recommended this web site by my cousin. I’m not sure
    whether this post is written by him as no one else know such detailed
    about my trouble. You are wonderful! Thanks!

    My webpage: GeeHii Keto Ingredients

  2. Wow! This blog looks exactly like my old one!

    It’s on a completely different topic but it has pretty much the same layout and design. Outstanding choice of colors!

    My website Bio Wellness CBD Review

  3. I really like and appreciate your article.

评论已关闭。