一、Django中间件
1.1 介绍
Django中的中间件是一个轻量级、底层的插件系统,介于request与response处理之间的一道处理过程(用来处理特定业务的请求和响应)。中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性。
我们可以使用中间件,在Django处理视图的不同阶段对输入或输出进行干预。
请求—中间件—响应:
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请求方法:
-
GET: 查询
-
POST: 添加或修改数据
-
PUT : 修改数据
-
DELETE: 删除数据
-
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)