Skip to content

Commit

Permalink
feat/mock_log (#218)
Browse files Browse the repository at this point in the history
* feat: add mock log api, django debug mode log sql

* feat: add mock log page

* feat: shorter project id

---------

Co-authored-by: rikasai233 <[email protected]>
  • Loading branch information
lihuacai168 and trumpchifan authored Jun 15, 2024
1 parent 2c71fbb commit 1b2b685
Show file tree
Hide file tree
Showing 8 changed files with 352 additions and 24 deletions.
33 changes: 19 additions & 14 deletions FasterRunner/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@

DEBUG = False


ALLOWED_HOSTS = ["*"]

# Token Settings, 30天过期
Expand Down Expand Up @@ -130,10 +129,8 @@

USE_I18N = True


USE_TZ = False


REST_FRAMEWORK = {
# 'DEFAULT_AUTHENTICATION_CLASSES': ['FasterRunner.auth.DeleteAuthenticator', 'FasterRunner.auth.Authenticator', ],
"DEFAULT_AUTHENTICATION_CLASSES": [
Expand Down Expand Up @@ -161,8 +158,12 @@
SWAGGER_SETTINGS = {
"DEFAULT_AUTO_SCHEMA_CLASS": "FasterRunner.swagger.CustomSwaggerAutoSchema",
# 基础样式
"SECURITY_DEFINITIONS": {
"basic": {"type": "basic"},
'SECURITY_DEFINITIONS': {
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header'
}
},
# 如果需要登录才能够查看接口文档, 登录的链接使用restframework自带的.
# 'LOGIN_URL': 'rest_framework:login',
Expand Down Expand Up @@ -220,10 +221,10 @@
logger.add(
f"logs/{level.lower()}.log",
format="{time:YYYY-MM-DD HH:mm:ss.SSS}"
" [pid:{process} -> thread:{thread.name}]"
" {level}"
" [{name}:{function}:{line}]"
" {message}",
" [pid:{process} -> thread:{thread.name}]"
" {level}"
" [{name}:{function}:{line}]"
" {message}",
level=level,
rotation="00:00",
retention="14 days",
Expand All @@ -239,7 +240,7 @@
"color": {
"()": "colorlog.ColoredFormatter",
"format": "%(green)s%(asctime)s [%(request_id)s] %(name)s %(log_color)s%(levelname)s [pid:%(process)d] "
"[%(filename)s->%(funcName)s:%(lineno)s] %(cyan)s%(message)s",
"[%(filename)s->%(funcName)s:%(lineno)s] %(cyan)s%(message)s",
"log_colors": {
"DEBUG": "black",
"INFO": "white",
Expand Down Expand Up @@ -300,6 +301,13 @@
"level": "INFO",
"propagate": True,
},

'django.db.backends': {
'level': 'DEBUG',
'handlers': ['console'],
"propagate": False,
},

"fastrunner": {
"handlers": ["default", "console", "error", "db"],
"level": "INFO",
Expand Down Expand Up @@ -331,11 +339,9 @@
GENERATE_REQUEST_ID_IF_NOT_IN_HEADER = True
REQUEST_ID_RESPONSE_HEADER = "TRACE-ID"


# https://github.com/celery/celery/issues/4796
DJANGO_CELERY_BEAT_TZ_AWARE = False


# 邮箱配置
EMAIL_USE_SSL = True
EMAIL_HOST = os.environ.get("EMAIL_HOST", "smtp.qq.com") # 如果是 163 改成 smtp.163.com
Expand Down Expand Up @@ -377,6 +383,5 @@
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"


# mock配置, 如果是性能测试环境,就设置为1,会关闭写日志, db
IS_PERF = os.environ.get("IS_PERF", "0")
IS_PERF = os.environ.get("IS_PERF", "0")
7 changes: 4 additions & 3 deletions FasterRunner/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from rest_framework_jwt.views import obtain_jwt_token

from fastrunner.views import run_all_auto_case
from mock.views import MockAPIView, MockAPIViewset, MockProjectViewSet
from mock.views import MockAPIView, MockAPIViewset, MockProjectViewSet, MockAPILogViewSet
from system import views as system_views

schema_view = get_schema_view(
Expand All @@ -35,7 +35,7 @@
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=(permissions.AllowAny,),
permission_classes=[permissions.AllowAny,],
authentication_classes=[],
)
system_router = DefaultRouter()
Expand All @@ -47,6 +47,7 @@

mock_project_router = DefaultRouter()
mock_project_router.register(r"mock_project", MockProjectViewSet)
mock_project_router.register(r"mock_log", MockAPILogViewSet)

urlpatterns = [
path("api/mock/", include(mock_project_router.urls)),
Expand All @@ -70,7 +71,7 @@
path("get_report_url/", run_all_auto_case.get_report_url, name="get_report_url"),
# swagger
re_path(r"^swagger(?P<format>\.json|\.yaml)$", schema_view.without_ui(cache_timeout=0), name="schema-json"),
path("swagger/", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui"),
path("swagger/", schema_view.with_ui("swagger", cache_timeout=0), name="schema-swagger-ui",),
path("redoc/", schema_view.with_ui("redoc", cache_timeout=0), name="schema-redoc"),
re_path(r'^mock/(?P<project_id>\w+)(?P<path>/.*)$', MockAPIView.as_view())
]
11 changes: 10 additions & 1 deletion mock/models.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import random
import string
import time
import uuid

from django.db import models
Expand All @@ -8,8 +11,14 @@ def generate_uuid():
return uuid.uuid4().hex


def generate_short_id():
timestamp = int(time.time() * 1000)
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=5))
return f'{timestamp}{random_string}'


class MockProject(BaseTable):
project_id = models.CharField(max_length=100, unique=True, default=generate_uuid)
project_id = models.CharField(max_length=100, unique=True, default=generate_short_id)
project_name = models.CharField(max_length=100)
project_desc = models.CharField(max_length=100)
is_active = models.BooleanField(default=True)
Expand Down
11 changes: 10 additions & 1 deletion mock/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

from rest_framework import serializers

from .models import MockAPI, MockProject
from .models import MockAPI, MockProject, MockAPILog


class MockAPISerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -109,3 +109,12 @@ class Meta:
"update_time",
]
read_only_fields = ["id", "creator", "updater", "create_time", "update_time", "project_id"]


class MockAPILogSerializer(serializers.ModelSerializer):
api = MockAPISerializer()
project = MockProjectSerializer()

class Meta:
model = MockAPILog
fields = '__all__'
47 changes: 44 additions & 3 deletions mock/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@
import traceback
import types
import uuid
from datetime import datetime


from django_filters import rest_framework as filters
from django_filters.rest_framework import DjangoFilterBackend
from drf_yasg.utils import swagger_auto_schema
from rest_framework import status, viewsets
from rest_framework.filters import SearchFilter
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.views import APIView
from django.conf import settings

from FasterRunner.customer_swagger import CustomSwaggerAutoSchema
from .models import MockAPI, MockAPILog, MockProject
from .serializers import MockAPISerializer, MockProjectSerializer
from .serializers import MockAPISerializer, MockProjectSerializer, MockAPILogSerializer

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -147,8 +150,23 @@ def load_and_execute(module_name, code, method_name, request) -> Response:
except Exception as e:
raise e


def convert_to_kv(input_dict):
"""
将包含元组值的字典转换为键值对字典。
参数:
input_dict (dict): 输入字典,其中值为包含两个元素的元组。
返回:
dict: 简单的键值对字典。
"""
return {value[0]: value[1] for key, value in input_dict.items()}


def process(path, project_id, request: Request):
try:
req_time = datetime.now()
if settings.IS_PERF == '0':
logger.info(f"request path: {request.get_full_path()}")

Expand All @@ -157,7 +175,7 @@ def process(path, project_id, request: Request):
"path": path,
"mock_server_full_path": request.get_full_path(),
"body": request.data,
"headers": request.headers._store,
"headers": convert_to_kv(request.headers._store),
"query_params": request.query_params,
}
if settings.IS_PERF == '0':
Expand All @@ -178,7 +196,7 @@ def process(path, project_id, request: Request):
response_obj = {
"status": response.status_code,
"body": response.data,
"headers": response.headers._store,
"headers": convert_to_kv(response.headers._store),
}
if settings.IS_PERF == '0':
logger.info(f"response_obj: {json.dumps(response_obj, indent=4)}")
Expand All @@ -187,6 +205,7 @@ def process(path, project_id, request: Request):
request_id=request_id,
api_id=mock_api.api_id,
project_id=mock_api.project,
create_time=req_time
)
log_obj.response_obj = response_obj
log_obj.save()
Expand Down Expand Up @@ -245,3 +264,25 @@ class MockProjectViewSet(viewsets.ModelViewSet):
serializer_class = MockProjectSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = MockProjectFilter


class MockAPILogFilter(filters.FilterSet):
request_id = filters.CharFilter(lookup_expr="icontains")
request_path = filters.CharFilter(method="filter_by_request_path")

class Meta:
model = MockAPILog
fields = ["request_id", "request_path"]

def filter_by_request_path(self, queryset, name, value):
return queryset.filter(api__request_path__icontains=value)


class MockAPILogViewSet(viewsets.ModelViewSet):
swagger_tag = 'Mock API Log CRUD'
swagger_schema = CustomSwaggerAutoSchema
queryset = MockAPILog.objects.select_related('api', 'project').all()
serializer_class = MockAPILogSerializer
filter_backends = [DjangoFilterBackend, SearchFilter]
filterset_class = MockAPILogFilter
search_fields = ['request_id', 'api__request_path']
3 changes: 2 additions & 1 deletion web/src/pages/home/components/Side.vue
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export default {
icon: 'el-icon-s-help',
children: [ // 子菜单项
{name: "Mock 项目", url: "MockProject", icon: 'el-icon-folder-opened'},
{name: "Mock APIs", url: "MockAPIs", icon: 'el-icon-document-copy'}
{name: "Mock APIs", url: "MockAPIs", icon: 'el-icon-document-copy'},
{name: "Mock Log", url: "MockLog", icon: 'el-icon-data-board'}
]
}
]
Expand Down
Loading

0 comments on commit 1b2b685

Please sign in to comment.