项目目录结构
---study_drf
---study_drf
---settings.py
---utils
---log.py
---exception.py
项目依赖
Django==3.2 django-cors-headers==3.5.0 djangorestframework==3.12.2 djangorestframework-jwt==1.11.0 mysqlclient==2.1.1 pinyin==0.4.0 #拼音处理 PyJWT==1.7.1 fpdf==1.7.2 #生成pdf文件 APScheduler==3.10.1 #轻量级定时任务 redis==4.6.0 #操作redis数据库的
要使用drf的Response做为响应对象,需要先注册app
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',#使用rest_framework的Response需要注册
]
一、utils/log.py
1、setting.py配置日志
#######--配置日志,
#1、配置日志要保存的文件夹,创建文件夹
import time
BASE_LOG_DIR = os.path.join(BASE_DIR, 'logs')
ERROR_PATH_DIR = os.path.join(BASE_LOG_DIR,'error')
if not os.path.exists(ERROR_PATH_DIR):
#如果logs/error/不存在,递归创建目录
os.makedirs(ERROR_PATH_DIR)
ALL_PATH_DIR = os.path.join(BASE_LOG_DIR,'all')
if not os.path.exists(ALL_PATH_DIR):
#如果logs/all/目录不存在,就递归创建(如果logs目录不存在,也会创建)
os.makedirs(ALL_PATH_DIR)
#2、相关的日志配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False, # 设置已存在的logger不失效
'filters': {
},
'formatters': {
'standard': {
'format': '[%(asctime)s][%(levelname)s][%(filename)s:%(lineno)d:%(funcName)s]:%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
},
'simple': {
'format': '[%(asctime)s][%(levelname)s]:%(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': { #按照文件大小分割日志,将所有的日志信息都保存在这里
'level': 'DEBUG',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_LOG_DIR,'all', 'debug.log'),
'maxBytes': 1024 * 1024 * 50, # 日志大小50M
'backupCount': 5,
'formatter': 'standard',
'encoding': 'utf-8',
},
#os.path.join(BASE_LOG_DIR,'error',f"{time.strftime('%Y-%m-%d')}.log")
'time_file':{#按照时间分割日志,每周一新增一个日志文件,存error等级以上的日志
'level': 'INFO',#日志的等级
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename':os.path.join(BASE_LOG_DIR,'error',f"{time.strftime('%Y-%m-%d')}.log") ,#日志的文件名
'when': 'D', #时间单位,S,M,H,D,'W0'(星期1),'W6'(星期天)
'interval': 1,
'backupCount': 5, #备份数量
'formatter': 'standard', #使用的日志格式
'encoding': 'utf-8',
}
},
'loggers': {
#INFO以上日志,打印在console,也写到以文件大小分割的日志中
'django': {
'handlers': ['console','file'],#handlers中存在的配置
'level': 'INFO',
'propagate': True
},
#error以上的日志写到按时间分割的日志文件中,同时打印在控制台
'django.request': {
'handlers': ['time_file'],#handlers中存在的配置
'level': 'ERROR',
'propagate': True
},
},
}
2、utils/log.py,拿到配置的日志处理对象,实现手动记录日志
#手动写日志
import logging
info_logger = logging.getLogger('django') #根据loggers配置handlers,将日志写到指定的地方
error_logger = logging.getLogger('django.request') #根据loggers配置handlers,将日志写到指定的地方
if __name__ == '__main__':
#简单的使用例子,使用这个方法记录日志,可以记录详细的日志信息(在视图中使用)
error_logger.exception(f'{__file__}/Test_log,method=post,error,django.request')
二、utils/exception.py
1.1、重写自己的drf全局异常模块
from rest_framework.views import exception_handler
from rest_framework.response import Response
from rest_framework import status
from utils.log import error_logger #写日志
#配置drf的全局异常处理,需要在settings中配置
def _exception_handler(exc,context):
#exc是报错的异常对象,context是哪个模块的函数出现报错
response = exception_handler(exc,context)
# print(exc.detail.get('error'),exc.detail.get('code'))
# print(exc,'异常对象')
# print(context,'报错函数')
# print(response,'返回对象')
#response有两个结果:一个是None(系统处理),一个django处理后(但格式不符合要求)
if not response:
#当然可以在这里进行精细的捕获
#将错误信息写到日志文件中,使用这个方法,可以把更多信息记录起来(不要使用error方法,记录的信息不详细)
error_logger.exception(exc)
return Response(data={'error':'系统内部错误,请稍后再重试','code':500},
status=status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
#djang处理的
if type(response.data) == dict:
detail = response.data.get('detail')
elif type(response.data) == list:
#特殊情况处理
detail = response.data
if len(detail) <=1:
return Response(data={'error':detail[0], 'code': 400},
status=response.status_code)
else:
return Response(data={'error': detail, 'code': 416},
status=response.status_code)
else:
detail = response.data
if detail:
if '未找到。' == detail:
detail = '查询不到该数据信息'
if '无效页面。' == detail:
detail = '该页面的数据查询不到,请确定要查询的页面数据是否存在'
return Response(data={'error':detail,'code':400},status=response.status_code)
else:
#我在写认证时,raise AuthenticationFailed({'code': 410, 'msg': msg,'type':'报错'})
#拿到这里抛出异常的信息和code
return Response(data={'error':exc.detail.get('error'),'code':exc.detail.get('code')},status=response.status_code)
1.2、settings.py 在配置文件中使用全局异常处理
#### drf的配置,全局配置
REST_FRAMEWORK = {
#2、配置全局异常处理
'EXCEPTION_HANDLER': 'utils.exception._exception_handler',
}