Django REST framework 日志(重写drf_api_logger)

这篇具有很好参考价值的文章主要介绍了Django REST framework 日志(重写drf_api_logger)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Django REST framework 日志

默认的drf-api-logger没有保存用户并且没有获取日志的接口
本文通过重写drf-api-logger增加访问用户及获取日志的接口
并且增加定时器删除日志

drf_api_logger

文档

优点:您可以将 API 信息记录到数据库中或侦听不同用例的记录器信号,也可以同时执行这两项操作。
记录器使用单独的线程来运行,因此不会影响 API 响应时间。

安装

pip install drf-api-logger

配置

# settings.py
INSTALLED_APPS = [
	'...',
    'drf_api_logger',
]

MIDDLEWARE = [
	'...',
    'drf_api_logger.middleware.api_logger_middleware.APILoggerMiddleware',
]

# drf-api-logger 配置
DRF_API_LOGGER_DATABASE = True # 是否记录到数据库
DRF_API_LOGGER_SIGNAL = True # 是否发送信号
DRF_API_LOGGER_PATH_TYPE = 'ABSOLUTE' # 路径类型
DRF_API_LOGGER_SKIP_URL_NAME = [] # 跳过的url
DRF_API_LOGGER_SKIP_NAMESPACE = [] # 跳过的命名空间(应用程序)
DRF_API_LOGGER_METHODS = [] # 跳过的方法
DRF_API_LOGGER_STATUS_CODES = [] # 跳过的状态码
DRF_API_LOGGER_EXCLUDE_KEYS = ['password', 'token', 'access', 'refresh','AUTHORIZATION'] # 在日志中隐藏敏感数据
DRF_LOGGER_QUEUE_MAX_SIZE = 50 # 日志队列最大长度
DRF_LOGGER_QUEUE_FLUSH_INTERVAL = 10 # 日志队列刷新间隔

查看

可以在 Django 管理面板的管理面板查看。

重写

drf_api_logger其实就是django的一个应用
复制drf_api_logger的源码后执行 pip uninstall drf-api-logger 删除模块(不删也没事)

添加用户信息

模型中添加用户字段

# models.py
from django.contrib.auth import get_user_model
User = get_user_model()
···
class APILogsModel(BaseModel):
	···
	user = models.ForeignKey(User,null=True, blank=True, on_delete=models.CASCADE,verbose_name="用户",help_text="用户")
	···

中间件中修改添加数据库时的方法()文章来源地址https://www.toymoban.com/news/detail-510180.html

# middleware\api_logger_middleware.py
# 导入你的验证Token方法,我使用的是Django-Rest-Knox
from knox.auth import TokenAuthentication

class APILoggerMiddleware:
···
	def __call__(self, request):
 		···
		# 获取用户模型
		try:
		    user,_ = TokenAuthentication().authenticate(request)
		except Exception as e: 
		    user = None
		
		data = dict(
		  user=user,
		  api=mask_sensitive_data(api, mask_api_parameters=True),
		  headers=mask_sensitive_data(headers),
		  body=mask_sensitive_data(request_data),
		  method=method,
		  client_ip_address=get_client_ip(request),
		  response=mask_sensitive_data(response_body),
		  status_code=response.status_code,
		  execution_time=time.time() - start_time,
		  added_on=timezone.now()
		)
		···

添加获取日志的接口

编写序列化器
# serializers.py
from .models import APILogsModel
from rest_framework import serializers
import json

class Jsonserializer(serializers.CharField):
    """编写一个序列化字段,将字符串转为json对象"""
    def to_representation(self, value):
        try:
            return json.loads(value)
        except Exception as e:
            return value
        

class APILogsSerializers(serializers.ModelSerializer):
    # 将json字符串转为json对象,方便前端展示,否则前端展示的是字符串

    headers = Jsonserializer()
    body = Jsonserializer()
    response = Jsonserializer()
    
    class Meta:
        model = APILogsModel
        fields = '__all__'

编写视图

# views.py
from .serializers import APILogsSerializers
from .models import APILogsModel
from rest_framework import viewsets
from rest_framework import permissions

class APILogsViewSet(viewsets.ReadOnlyModelViewSet):
    """添加获取日志列表的视图函数"""
    queryset = APILogsModel.objects.all()
    serializer_class = APILogsSerializers
    permission_classes = [permissions.IsAdminUser]

编写路由

# urls.py
from django.urls import path
from rest_framework import routers
router = routers.DefaultRouter()
from . import views
router.register("log",views.APILogsViewSet,"log")
urlpatterns = []
urlpatterns += router.urls

完整代码

# models.py
# middleware\api_logger_middleware.py
from django.db import models

from drf_api_logger.utils import database_log_enabled
from django.contrib.auth import get_user_model

User = get_user_model()

if database_log_enabled():
    """
    Load models only if DRF_API_LOGGER_DATABASE is True
    """
    class BaseModel(models.Model):
        id = models.BigAutoField(primary_key=True)

        added_on = models.DateTimeField()

        def __str__(self):
            return str(self.id)

        class Meta:
            abstract = True
            ordering = ('-added_on',)


    class APILogsModel(BaseModel):
        api = models.CharField(max_length=1024, help_text='API URL')
        user = models.ForeignKey(User,null=True, blank=True, on_delete=models.CASCADE,verbose_name="用户",help_text="用户")
        headers = models.TextField()
        body = models.TextField()
        method = models.CharField(max_length=10, db_index=True)
        client_ip_address = models.CharField(max_length=50)
        response = models.TextField()
        status_code = models.PositiveSmallIntegerField(help_text='Response status code', db_index=True)
        execution_time = models.DecimalField(decimal_places=5, max_digits=8,
                                             help_text='Server execution time (Not complete response time.)')

        def __str__(self):
            return self.api

        class Meta:
            db_table = 'drf_api_logs'
            verbose_name = 'API Log'
            verbose_name_plural = 'API Logs'


import json
import time
import re
from django.conf import settings
from django.urls import resolve
from django.utils import timezone

from drf_api_logger import API_LOGGER_SIGNAL
from drf_api_logger.start_logger_when_server_starts import LOGGER_THREAD
from drf_api_logger.utils import get_headers, get_client_ip, mask_sensitive_data

from knox.auth import TokenAuthentication
from rest_framework import exceptions

"""
File: api_logger_middleware.py
Class: APILoggerMiddleware
重写以在日志中记录用户信息
"""


class APILoggerMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # One-time configuration and initialization.

        self.DRF_API_LOGGER_DATABASE = False
        if hasattr(settings, 'DRF_API_LOGGER_DATABASE'):
            self.DRF_API_LOGGER_DATABASE = settings.DRF_API_LOGGER_DATABASE

        self.DRF_API_LOGGER_SIGNAL = False
        if hasattr(settings, 'DRF_API_LOGGER_SIGNAL'):
            self.DRF_API_LOGGER_SIGNAL = settings.DRF_API_LOGGER_SIGNAL

        self.DRF_API_LOGGER_PATH_TYPE = 'ABSOLUTE'
        if hasattr(settings, 'DRF_API_LOGGER_PATH_TYPE'):
            if settings.DRF_API_LOGGER_PATH_TYPE in ['ABSOLUTE', 'RAW_URI', 'FULL_PATH']:
                self.DRF_API_LOGGER_PATH_TYPE = settings.DRF_API_LOGGER_PATH_TYPE

        self.DRF_API_LOGGER_SKIP_URL_NAME = []
        if hasattr(settings, 'DRF_API_LOGGER_SKIP_URL_NAME'):
            if type(settings.DRF_API_LOGGER_SKIP_URL_NAME) is tuple or type(
                    settings.DRF_API_LOGGER_SKIP_URL_NAME) is list:
                self.DRF_API_LOGGER_SKIP_URL_NAME = settings.DRF_API_LOGGER_SKIP_URL_NAME

        self.DRF_API_LOGGER_SKIP_NAMESPACE = []
        if hasattr(settings, 'DRF_API_LOGGER_SKIP_NAMESPACE'):
            if type(settings.DRF_API_LOGGER_SKIP_NAMESPACE) is tuple or type(
                    settings.DRF_API_LOGGER_SKIP_NAMESPACE) is list:
                self.DRF_API_LOGGER_SKIP_NAMESPACE = settings.DRF_API_LOGGER_SKIP_NAMESPACE

        self.DRF_API_LOGGER_METHODS = []
        if hasattr(settings, 'DRF_API_LOGGER_METHODS'):
            if type(settings.DRF_API_LOGGER_METHODS) is tuple or type(
                    settings.DRF_API_LOGGER_METHODS) is list:
                self.DRF_API_LOGGER_METHODS = settings.DRF_API_LOGGER_METHODS

        self.DRF_API_LOGGER_STATUS_CODES = []
        if hasattr(settings, 'DRF_API_LOGGER_STATUS_CODES'):
            if type(settings.DRF_API_LOGGER_STATUS_CODES) is tuple or type(
                    settings.DRF_API_LOGGER_STATUS_CODES) is list:
                self.DRF_API_LOGGER_STATUS_CODES = settings.DRF_API_LOGGER_STATUS_CODES

    def __call__(self, request):

        # Run only if logger is enabled.
        if self.DRF_API_LOGGER_DATABASE or self.DRF_API_LOGGER_SIGNAL:

            url_name = resolve(request.path_info).url_name
            namespace = resolve(request.path_info).namespace

            # Always skip Admin panel
            if namespace == 'admin':
                return self.get_response(request)

            # Skip for url name
            if url_name in self.DRF_API_LOGGER_SKIP_URL_NAME:
                return self.get_response(request)

            # Skip entire app using namespace
            if namespace in self.DRF_API_LOGGER_SKIP_NAMESPACE:
                return self.get_response(request)

            start_time = time.time()
            request_data = ''
            try:
                request_data = json.loads(request.body) if request.body else ''
            except:
                pass

            # Code to be executed for each request before
            # the view (and later middleware) are called.
            response = self.get_response(request)

            # Only log required status codes if matching
            if self.DRF_API_LOGGER_STATUS_CODES and response.status_code not in self.DRF_API_LOGGER_STATUS_CODES:
                return response

            # Code to be executed for each request/response after
            # the view is called.

            headers = get_headers(request=request)
            method = request.method

            # Log only registered methods if available.
            if len(self.DRF_API_LOGGER_METHODS) > 0 and method not in self.DRF_API_LOGGER_METHODS:
                return response

            if response.get('content-type') in ('application/json', 'application/vnd.api+json', 'application/gzip'):
                
                if response.get('content-type') == 'application/gzip':
                    response_body = '** GZIP Archive **'
                elif getattr(response, 'streaming', False):
                    response_body = '** Streaming **'
                else:
                    if type(response.content) == bytes:
                        response_body = json.loads(response.content.decode())
                    else:
                        response_body = json.loads(response.content)
                if self.DRF_API_LOGGER_PATH_TYPE == 'ABSOLUTE':
                    api = request.build_absolute_uri()
                elif self.DRF_API_LOGGER_PATH_TYPE == 'FULL_PATH':
                    api = request.get_full_path()
                elif self.DRF_API_LOGGER_PATH_TYPE == 'RAW_URI':
                    api = request.get_raw_uri()
                else:
                    api = request.build_absolute_uri()
                
                
                try:
                    user,_ = TokenAuthentication().authenticate(request)
                except Exception as e: 
                    user = None
                
                data = dict(
                    user=user,
                    api=mask_sensitive_data(api, mask_api_parameters=True),
                    headers=mask_sensitive_data(headers),
                    body=mask_sensitive_data(request_data),
                    method=method,
                    client_ip_address=get_client_ip(request),
                    response=mask_sensitive_data(response_body),
                    status_code=response.status_code,
                    execution_time=time.time() - start_time,
                    added_on=timezone.now()
                )
                if self.DRF_API_LOGGER_DATABASE:
                    if LOGGER_THREAD:
                        d = data.copy()
                        d['headers'] = json.dumps(d['headers'], indent=4, ensure_ascii=False)
                        if request_data:
                            d['body'] = json.dumps(d['body'], indent=4, ensure_ascii=False)
                        d['response'] = json.dumps(d['response'], indent=4, ensure_ascii=False)
                        LOGGER_THREAD.put_log_data(data=d)
                if self.DRF_API_LOGGER_SIGNAL:
                    API_LOGGER_SIGNAL.listen(**data)
            else:
                return response
        else:
            response = self.get_response(request)
        return response

到了这里,关于Django REST framework 日志(重写drf_api_logger)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 深入探索 Django Rest Framework

    这篇文章会详细介绍Django REST Framework的核心组成部分,包括Serializers、ViewSets、Routers、权限和认证系统以及测试和调试工具。文章从基础开始,逐步深入,旨在帮助读者掌握使用Django REST Framework构建复杂API的技能。 Django REST框架,通常简称为DRF,是一个强大而灵活的Web API工具

    2024年02月15日
    浏览(40)
  • Django Rest_Framework(一)

    在开发Web应用中,有两种应用模式: 前后端不分离[客户端看到的内容和所有界面效果都是由服务端提供出来的。] 前后端分离【把前端的界面效果(html,css,js分离到另一个服务端或另一个目录下,python服务端只需要返回数据即可)】 前端形成一个独立的网站/独立的地址,服

    2024年02月14日
    浏览(39)
  • Django rest framework基本知识

    使用pycharm生成Django项目后,会生成工程目录和app目录 工程目录下5个文件,settings.py是全局配置相关的  urls.py是路有相关的 app相关的目录    models.py 数据库ORM对应的模型类  serializers.py 序列化与反序列化处理    views.py 根据request进行业务逻辑处理,返回response    admin.p

    2024年02月09日
    浏览(42)
  • Django Rest_Framework(三)

    为了方便接下来的学习,我们创建一个新的子应用 opt 注册子应用 总路由,代码: 子路由,代码: 因为接下来的认证组件中需要使用到登陆功能,所以我们使用django内置admin站点并创建一个管理员. admin运营站点的访问地址:http://127.0.0.1:8000/admin 创建管理员以后,访问admin站

    2024年02月14日
    浏览(51)
  • Django Rest_Framework(二)

    什么时候声明的序列化器需要继承序列化器基类Serializer,什么时候继承模型序列化器类ModelSerializer? 看数据是否从mysql数据库中获取,如果是则使用ModelSerializer,不是则使用Serializer drf除了在数据序列化部分简写代码以外,还在视图中提供了简写操作。所以在django原有的dja

    2024年02月14日
    浏览(35)
  • django rest framework 学习笔记2

    注意:该文章部分摘抄之百度,仅当做学习笔记供小白使用,若侵权请联系删除! 显示关联表的数据,本示例会显示所有的关联的数据信息 读取到的结果器数据关联的为数字,此时需要进行一些操作可以读到正确数据 方法1: source=\\\'字段名.关联属性值\\\' 方法2 : 返回其属性值

    2024年02月20日
    浏览(37)
  • 【后端】Django与Django REST Framework的结合使用

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 随着开发语言及人工智能工具的普及,使得越来越多的人会主动学习使用一些开发语言,本文主要介绍Django与Django REST Framework的结合使用。 创建Django项目通常包括以下步骤: 安装Django : 首先,确保你的

    2024年04月26日
    浏览(35)
  • 【django2.0之Rest_Framework框架一】rest_framework序列器介绍

    Django RestFramework(简称DRF) 提供了序列化器Serialzier的定义,可以帮助我们简化序列化与反序列化的过程,不仅如此,还提供丰富的类视图、扩展类、视图集来简化视图的编写工作。REST framework还提供了认证、权限、限流、过滤、分页、接口文档等功能支持。 github地址: https://

    2024年02月07日
    浏览(42)
  • 使用Django Rest framework搭建Blog

    在前面的Blog例子中我们使用的是GraphQL, 虽然GraphQL的使用处于上升趋势,但是Rest API还是使用的更广泛一些. 所以还是决定回到传统的rest api framework上来, Django rest framework的官网上给了一个很好用的QuickStart, 我参考Quick Start将前面的Blog的例子用DRF(Django Rest Framework)重新构筑一遍

    2023年04月19日
    浏览(40)
  • django rest framework 学习笔记-实战商城

     01项目环境搭建_哔哩哔哩_bilibili  本博客借鉴至大佬的视频学习笔记 创建apps文件夹放入的上面应用  创建shop数据库 mysql create database shop charset=utf8; Query OK, 1 row affected, 1 warning (0.01 sec)  公共表设计: 用户表结构设计  执行迁移文件,生成表结构 E:desktopmy_drfMyShoppython manag

    2024年02月22日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包