后台配置(封装logger+全局异常处理)

【一】环境变量

# 环境变量操作:小luffyapiBASE_DIR与apps文件夹都要添加到环境变量
import sys
sys.path.insert(0, BASE_DIR)
APPS_DIR = os.path.join(BASE_DIR, 'apps')
sys.path.insert(1, APPS_DIR)

【二】封装logger

# 真实项目上线后,日志文件打印级别不能过低,因为一次日志记录就是一次文件io操作
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
        },
        'simple': {
            'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
        },
    },
    'filters': {
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'handlers': {
        'console': {
       	    #打印在控制台上
            # 实际开发建议使用WARNING
            'level': 'DEBUG',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
            'formatter': 'simple'
        },
        'file': {
        	#写到文件里
            # 实际开发建议使用ERROR
            'level': 'INFO',
            'class': 'logging.handlers.RotatingFileHandler',
            # 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi
            'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"),
            # 日志文件的最大值,这里我们设置300M
            'maxBytes': 300 * 1024 * 1024,
            # 日志文件的数量,设置最大日志数量为10
            'backupCount': 10,
            # 日志格式:详细格式
            'formatter': 'verbose',
            # 文件内容编码
            'encoding': 'utf-8'
        },
    },
    # 日志对象
    'loggers': {
        'django': {
            'handlers': ['console', 'file'],
            'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统
        },
    }
}

  • utils/common_logger.py
import logging
logger = logging.getLogger('django')
  • 以后在任何位置导入即可

【三】封装项目异常处理并可以记录日志

  • utils/exception.py
from rest_framework.views import exception_handler as drf_exception_handler
from rest_framework.response import Response
from utils.common_logger import logger


# 自定义异常类,内置DRF不够
class NoPermissionException(Exception):
    pass
def exception_handler(exc, context):
    # 记录日志
    request= context.get('request')
    view= context.get('view')
    ip= request.META.get('REMOTE_ADDR')
    path=request.get_full_path()
    method= request.method
    user_id=request.user.id or '[匿名用户]'

    logger.error(f'操作出错:{str(exc)},ip地址为:{ip},请求方式是:{method},请求地址是:{path},用户为:{user_id}'
                 f',视图类为:{str(view)}')
    res = drf_exception_handler(exc, context)
    if res:
        # drf异常
        if isinstance(res.data, dict):
            err = res.data.get('detail')
        elif isinstance(res.data, list):
            err = res.data[0]
        else:
            err = "服务异常请稍后再试 -drf"
        response = Response({'code': 999, 'msg': err})
    else:
        # 更细粒度异常
        if isinstance(exc, ZeroDivisionError):
            err = "数据操作异常,除以0了"
            code = 909
        elif isinstance(exc,  NoPermissionException):
            err = f"没有操作权限:{str(exc)}"
            code = 906
        else:
            err = f"系统错误:{str(exc)}"
            code = 909
        response = Response({'code': code, 'msg': err})
    return response

  • settings配置
REST_FRAMEWORK = {
    'EXCEPTION_HANDLER': 'utils.common_exception.exception_handler',
}
  • 测试
  • 可以自定义异常
from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.exceptions import ValidationError, AuthenticationFailed

from utils.common_exception import NoPermissionException

class ExceptionView(APIView):
    def get(self, request):
        # raise ValidationError('校验失败')
        # raise AuthenticationFailed('认证失败')
        # raise NoPermissionException()
        try:
        # 核心逻辑
            if 某个条件不符合:  # 或断言
                raise NoPermissionException
            except NoPermissionException as e:
                # return Response({'code':888,'msg':'错误'})
                raise NoPermissionException('没有权限')
            except AAException as e:
                raise NoPermissionException('没有权限')
                return Response('ok')

image-20240510161145887

【四】封装Response

# 基于drf的Response二次封装,使用更便捷
-APIResponse()--->{code:100,msg:成功}
-APIResponse(code=101,msg='失败')--->{code:101,msg:失败}
-APIResponse(username=lqz,token:adsfa.as.ss,icon:头像地址)--->{code:100,msg:成功,username:lqz,token:adsfa.as.ss,icon:头像地址}

-APIResponse(results=[{},{}])--->{code:100,msg:成功,results=[{},{}]}

-APIResponse(headers={})--->{code:100,msg:成功,results=[{},{}]}
  • utils.commom_response
class APIResponse(Response):
    def __init__(self, code=100, msg='成功', status=None, headers=None, **kwargs):
        data = {'code': code, 'msg': msg}
        if kwargs:
            data.update(kwargs)
        # 继承Response
        super().__init__(data=data, status=status, headers=headers)
  • 测试
class ResponseView(APIView):
   
    def get(self, request,*args,**kwargs):
        return APIResponse(result=[{},{}])

image-20240510164104409

posted @ 2024-05-27 12:04  -半城烟雨  阅读(2)  评论(0编辑  收藏  举报