Django中间件与python日志模块 介绍

本文详细介绍了Django框架中的中间件系统,包括其配置、钩子函数及自定义实现,同时探讨了如何利用中间件实现请求黑名单、缓存数据等功能。此外,还深入解析了Python的日志模块,包括日志的四大部分:记录器、处理器、格式化器和过滤器,并提供了实际应用示例。
摘要由CSDN通过智能技术生成

一、Django中间件

1.1 介绍

Django中的中间件是一个轻量级、底层的插件系统,介于request与response处理之间的一道处理过程(用来处理特定业务的请求和响应)。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。

我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。

请求—中间件—响应:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SiFAbf47-1597452018811)(E:\07-notes\picture\01-请求_中间件_响应.png)]

1.1.1 中间件配置

定义好中间件后,需要在settings.py 文件中添加注册中间件

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',  # 安全中间件
    'django.contrib.sessions.middleware.SessionMiddleware', # 会话中间件
    'corsheaders.middleware.CorsMiddleware',  # 跨域请求中间件
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware', # 防伪造跨域请求中间件
    'django.contrib.auth.middleware.AuthenticationMiddleware', # 授权中间件
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

【拓展】HTTP请求方法:

  1. GET: 查询

  2. POST: 添加或修改数据

  3. PUT : 修改数据

  4. DELETE: 删除数据

  5. OPTIONS: 探测服务器是否支持跨域

    Access-Control-Allow[-Origin|Headers|Methods|Credentials]

    访问-控制-允许-[源|头|方法|认证]

1.1.2 中间件的钩子函数(生命周期):
process_request:  请求到达时,未到路由
process_view:     转到view处理函数之前
process_exception:  在view处理函数中抛出异常时
process_response:   在View响应之后,向客户返回(输出)之前
process_template_response:  响应之前,渲染模板之后
1.1.3 自定义中间件类
from django.utils.deprecation import MiddlewareMixin

class AuthUserMiddleware(MiddlewareMixin):
  def process_request(self, request):
    pass
  
  def process_view(self, request, view, *args, **kwargs):
    pass
  
  def process_exception(self, request, exception):
    pass

  def process_response(self, request, response):
    return response
1.1.4 通过中间件实现的功能
- 请求黑名单: 1秒内允许2次请求(正常),非正常,则在process_request()方法直接返回
- 提取授权用户的信息和权限验证:  提前配置请求路径和用户权限 
- 异常的友好提示
- 缓存数据(减少数据库的交互次数): process_response() 对response对象进行序列化和缓存。
- 记录日志(logging 模块)

1.2 线程方式实现缓存

import time
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
import threading

cached = {}


# 过期缓存监控的线程
class ExpireThread(threading.Thread):
    def clear(self, path, max_age):
        time.sleep(max_age)
        print('--checked--', path, cached.keys())
        if path in cached:
            cached.pop(path)

    def run(self):
        while True:
            time.sleep(1)
            print('--check--', cached.keys())
            for path, resp_max_age in cached.items():
                threading.Thread(target=self.clear,
                                 args=(path, resp_max_age[-1])).start()


class MyMiddleware(MiddlewareMixin):
    def process_request(self, request):
        print('--process_request---', request.path)
        # 判断当前请求路径是否在缓存中
        # 如果存在则,直接返回响应
        if request.path in cached:
            return cached[request.path][0]

    def process_view(self, request, view, *args, **kwargs):
        # args 表示 view函数的位置参数
        # kwargs 表示 view函数的关键参数
        print('---process_view---', args)

    def process_exception(self, request, exception):
        print(request.path, dir(exception))
        print('----process_exception----', exception)

        return HttpResponse('<h3>Error Page 500: %s</h3>' % str(exception))

    def process_response(self, request, response):
        print('---process_response---')
        if request.path not in cached:
          # 存入缓存(内存)
          print('---存入缓存---')
          cached[request.path] = (response, 3)
          print('---缓存---', cached)
          
        return response

ExpireThread().start()

注册中间件, 在settings.py文件中,修改MIDDLEWARE,如下所示:

MIDDLEWARE = [
	  ...
    'apiapp.middleware.MyMiddleware',
    'django.middleware.common.CommonMiddleware',
    ...
]

1.3 基于Redis实现缓存页面

from utils.caches import save_response, get_response, has_response
import pickle
import base64

class RedisCacheMiddleware(MiddlewareMixin):
    def process_request(self, request):
        print('--process_request---', request.path)
        # 判断当前请求路径是否在缓存中
        # 如果存在则,直接返回响应
        if has_response(request.path):
            resp_base_str = get_response(request.path)
            resp_bytes = base64.b32decode(resp_base_str.encode())
            response = pickle.loads(resp_bytes)

            return response

    def process_response(self, request, response):
        print('---process_response---')
        # 存入缓存(内存)
        if not has_response(request.path):
            resp_bytes = pickle.dumps(response) # 将python的任意对象序列化字段码
            # b'QABWGZDKMFXGO3ZONB2HI4BOOJSXG4DPNZZWKCSIOR2HAUTFONYG63TTMUFHCABJQFYQC7LRAIUFQCAAAAAF'
            resp_base64_str = base64.b32encode(resp_bytes).decode()
            save_response(request.path, resp_base64_str, 3)
        return response

二、日志模块

Python的日志模块是: logging

import logging

2.1 日志的四大部分

日志是用于记录(Logger)程序中的一些重要信息的,记录的信息按等级(Level)交给特定的处理器Handler按一定的格式(Formatter)进行处理(打印、文件保存、上传网络和发送邮件等)。

2.1.1 日志记录器 Logger
  • 记录日志信息的唯一对象, 根据不同的信息等级使用不同的记录方法
    • x.debug() 记录调试信息,logging.DEBUG (10)
    • x.info() 记录普通信息, logging.INFO (20)
    • x.warning() 记录警告信息, loggin.WARNING (30)
    • x.error() 记录错误信息, logging.ERROR (40)
    • x.critical() 记录严重错误的信息, loggin.CRITICAL (50)
  • 创建或获取记录器: logging.getLogger(name) 如果name为空,则默认为root
  • 记录器可以添加处理器和过滤器: x.addHandler(Handler())、 x.addFilter(Filter() )
2.1.2 日志处理器 Handler

作用: 将记录器记录的相关日志信息,进行特定业务的处理,如写入文件的FileHandler、发送邮件的 SMTPHandler、上传远程服务器的HTTPHandler等。

【注意】处理器可以设置日志格式化 Formatter: h.setFormatter(Formatter())

  • StreamHandler 流处理器, 控制台打印的
  • FileHandler 文件处理器,将日志消息写入到文件中
  • HTTPHandler 网络处理器,将日志消息上传到服务器
  • SMTPHandler 邮件处理器,将日志消息发送到指定邮件中。

日志记录器和处理器的关系

一个记录器可以添加多个处理器。

logger.addHandler(handler1)
logger.addHandler(handler2)
logger = logging.getLogger("network")
logger.setLevel(logging.INFO) 

handler1 = StreamHandler()
handler1.setLevel(logging.INFO)

handler2 = FileHandler("network.log")
handler1.setLevel(logging.WARNING)

logger.addHandler(handler1)
logger.addHandler(handler2)
2.1.3 日志格式化 Formatter

作用: 将记录日志信息进行格式化(可以选择重要的信息、或者自定义格式的信息)

经典的日志格式:

[ %(asctime)s - %(levelname)s < line %(lineno)s at %(pathname)s  > ] %(message)s

如果给定的日志格式中变量不能完成业务需求时,可以自定义:

[ %(asctime)s - %(username)s: %(ip)s < %(action)s > ] %(message)s

以上username、ip和action都属于自定义的格式变量,在记录器记录信息时指定extra 参数, 它是一个字典类型,key即为扩展的变量名,value即是它的值。

日志的格式:

     |  %(name)s            Name of the logger (logging channel) 记录器的名称
     |  %(levelno)s         Numeric logging level for the message (DEBUG, INFO,
     |                      WARNING, ERROR, CRITICAL) 数字形式的日志记录级别
     |  %(levelname)s       Text logging level for the message ("DEBUG", "INFO",
     |                      "WARNING", "ERROR", "CRITICAL") 日志记录级别的文本名称
     |  %(pathname)s        Full pathname of the source file where the logging
     |                      call was issued (if available) 执行日志记录调用的源文件的路径名称
     |  %(filename)s        Filename portion of pathname 执行日志记录调用的源文件的文件名称
     |  %(module)s          Module (name portion of filename) 执行日志记录调用的模块名称
     |  %(lineno)d          Source line number where the logging call was issued
     |                      (if available) 执行日志记录调用的行号
     |  %(funcName)s        Function name 执行日志记录调用的函数名称
     |  %(created)f         Time when the LogRecord was created (time.time()
     |                      return value) 执行日志记录的时间
     |  %(asctime)s         Textual time when the LogRecord was created 日期和时间
     |  %(msecs)d           Millisecond portion of the creation time 毫秒部分
     |  %(relativeCreated)d Time in milliseconds when the LogRecord was created,
     |                      relative to the time the logging module was loaded
     |                      (typically at application startup time)
     |  %(thread)d          Thread ID (if available) 线程ID
     |  %(threadName)s      Thread name (if available) 线程名称
     |  %(process)d         Process ID (if available) 进程ID
     |  %(message)s         The result of record.getMessage(), computed just as
     |                      the record is emitted 记录的消息

2.1.4 日志过滤器 Filter

作用: 过滤一些敏感的记录日志信息。

2.2 日志的应用

2.2.1 标准使用
#!/usr/bin/python3
import logging
from logging import StreamHandler, FileHandler, Formatter, Filter

class MsgFilter(Filter):
    def filter(self, record): # record记录对象
        # print(dir(record)) # 查看记录器里面的方法
        return record.msg.find('混沌') == -1

logger = logging.getLogger('middleware')
# 设置记录器的信息等级:DEBUG -> INFO -> WARNING -> ERROR -> CRITICAL
logger.setLevel(logging.INFO) # 记录器可以记录INFO及以上等级信息

# 添加日志过滤器
logger.addFilter(MsgFilter())

# 创建日志处理器
handler1 = StreamHandler() # 流处理器,标准输出
handler1.setLevel(logging.INFO)
handler1.setFormatter(Formatter(
    fmt='[ %(asctime)s - %(levelname)s] %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
))

# 向记录器添加处理器
logger.addHandler(handler1)

handler2 = FileHandler('error.log', encoding='utf-8')
handler2.setLevel(logging.ERROR)
handler2.setFormatter(Formatter(
    fmt="[ %(asctime)s - %(levelname)s < line %(lineno)s at %(pathname)s  > ] %(message)s",
    datefmt='%Y-%m-%d %H:%M:%S'
))
logger.addHandler(handler2)

if __name__ == '__main__':
    # x = logging.getLogger('middleware')  # 获取middleware的记录器
    logging.getLogger('middleware').debug('中午吃拉面')
    logging.getLogger('middleware').info('中午吃木桶饭')
    logging.getLogger('middleware').warning('中午吃凉皮')
    logging.getLogger('middleware').error('中午吃粥和夹馍')
    logging.getLogger('middleware').critical('下午吃混沌')

2.2.2 扩展格式化的使用
mylogger = logging.getLogger('sys_action')
mylogger.setLevel(logging.INFO)

myhandler = StreamHandler()
myhandler.setLevel(logging.INFO)
myhandler.setFormatter(Formatter(
    fmt="[ %(asctime)s - %(username)s: %(ip)s < %(action)s > ] %(message)s",
    datefmt='%Y-%m-%d %H:%M:%S'
))

mylogger.addHandler(myhandler)

if __name__ == '__main__':
    extra_info = {
        'username': 'admin',
        'ip': '10.36.172.110',
        'action': 'del category'
    }
    logging.getLogger('sys_action').debug('中午吃拉面', extra=extra_info)
    logging.getLogger('sys_action').info('中午吃木桶饭', extra=extra_info)
    logging.getLogger('sys_action').warning('中午吃凉皮',extra=extra_info)
    logging.getLogger('sys_action').error('中午吃粥和夹馍', extra=extra_info)
    logging.getLogger('sys_action').critical('下午吃混沌', extra=extra_info)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值