Merge remote-tracking branch 'origin/dev' into dev
# Conflicts: # backend/application/settings.pypull/91/MERGE
commit
d37955166d
|
@ -94,7 +94,6 @@ TEMPLATES = [
|
|||
|
||||
WSGI_APPLICATION = "application.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
|
||||
|
||||
|
@ -191,84 +190,72 @@ CHANNEL_LAYERS = {
|
|||
# ================================================= #
|
||||
|
||||
# log 配置部分BEGIN #
|
||||
SERVER_LOGS_FILE = os.path.join(BASE_DIR, "logs", "server.log")
|
||||
ERROR_LOGS_FILE = os.path.join(BASE_DIR, "logs", "error.log")
|
||||
LOGS_FILE = os.path.join(BASE_DIR, "logs")
|
||||
if not os.path.exists(os.path.join(BASE_DIR, "logs")):
|
||||
os.makedirs(os.path.join(BASE_DIR, "logs"))
|
||||
|
||||
# 格式:[2020-04-22 23:33:01][micoservice.apps.ready():16] [INFO] 这是一条日志:
|
||||
# 格式:[日期][模块.函数名称():行号] [级别] 信息
|
||||
STANDARD_LOG_FORMAT = (
|
||||
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||
)
|
||||
CONSOLE_LOG_FORMAT = (
|
||||
"[%(asctime)s][%(name)s.%(funcName)s():%(lineno)d] [%(levelname)s] %(message)s"
|
||||
)
|
||||
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
"disable_existing_loggers": False,
|
||||
"formatters": {
|
||||
"standard": {"format": STANDARD_LOG_FORMAT},
|
||||
"console": {
|
||||
"format": CONSOLE_LOG_FORMAT,
|
||||
"servers": {
|
||||
"format": "%(message)s",
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
"file": {
|
||||
"format": CONSOLE_LOG_FORMAT,
|
||||
"datefmt": "%Y-%m-%d %H:%M:%S",
|
||||
},
|
||||
}
|
||||
},
|
||||
"handlers": {
|
||||
"file": {
|
||||
"level": "INFO",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": SERVER_LOGS_FILE,
|
||||
"servers": {
|
||||
"logging_levels": ["info", "error", "warning"],
|
||||
"class": "dvadmin.utils.log.InterceptTimedRotatingFileHandler", # 这个路径看你本地放在哪里(下面的log文件)
|
||||
"filename": os.path.join(LOGS_FILE, "server.log"),
|
||||
"when": "D",
|
||||
"interval": 1,
|
||||
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||
"backupCount": 5, # 最多备份5个
|
||||
"formatter": "standard",
|
||||
"backupCount": 1,
|
||||
"formatter": "servers",
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
"error": {
|
||||
"level": "ERROR",
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"filename": ERROR_LOGS_FILE,
|
||||
"maxBytes": 1024 * 1024 * 100, # 100 MB
|
||||
"backupCount": 3, # 最多备份3个
|
||||
"formatter": "standard",
|
||||
"encoding": "utf-8",
|
||||
},
|
||||
"console": {
|
||||
"level": "INFO",
|
||||
"class": "logging.StreamHandler",
|
||||
"formatter": "console",
|
||||
},
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
# default日志
|
||||
"": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO",
|
||||
'django': {
|
||||
'handlers': ['servers'],
|
||||
'propagate': False,
|
||||
'level': "INFO"
|
||||
},
|
||||
"django": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
'': {
|
||||
'handlers': ['servers'],
|
||||
'propagate': False,
|
||||
'level': "ERROR"
|
||||
},
|
||||
"scripts": {
|
||||
"handlers": ["console", "error", "file"],
|
||||
"level": "INFO",
|
||||
"propagate": False,
|
||||
'celery': {
|
||||
'handlers': ['servers'],
|
||||
'propagate': False,
|
||||
'level': "INFO"
|
||||
},
|
||||
# 数据库相关日志
|
||||
"django.db.backends": {
|
||||
"handlers": [],
|
||||
"propagate": True,
|
||||
'django.db.backends': {
|
||||
'handlers': ['servers'],
|
||||
'propagate': False,
|
||||
'level': "INFO"
|
||||
},
|
||||
'django.request': {
|
||||
'handlers': ['servers'],
|
||||
'propagate': False,
|
||||
'level': "DEBUG"
|
||||
},
|
||||
"uvicorn.error": {
|
||||
"level": "INFO",
|
||||
"handlers": ["servers"],
|
||||
},
|
||||
"uvicorn.access": {
|
||||
"handlers": ["servers"],
|
||||
"level": "INFO",
|
||||
"propagate": False
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
# ================================================= #
|
||||
# *************** REST_FRAMEWORK配置 *************** #
|
||||
# ================================================= #
|
||||
|
@ -307,7 +294,7 @@ from datetime import timedelta
|
|||
|
||||
SIMPLE_JWT = {
|
||||
# token有效时长
|
||||
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=1),
|
||||
"ACCESS_TOKEN_LIFETIME": timedelta(days=1),
|
||||
# token刷新后的有效时间
|
||||
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
|
||||
# 设置前缀
|
||||
|
@ -390,8 +377,6 @@ SYSTEM_CONFIG = {}
|
|||
# 字典配置
|
||||
DICTIONARY_CONFIG = {}
|
||||
|
||||
|
||||
|
||||
# ================================================= #
|
||||
# ******************** 插件配置 ******************** #
|
||||
# ================================================= #
|
||||
|
@ -403,8 +388,11 @@ PLUGINS_URL_PATTERNS = []
|
|||
# 例如:
|
||||
# from dvadmin_upgrade_center.settings import * # 升级中心
|
||||
# from dvadmin_celery.settings import * # celery 异步任务
|
||||
# from dvadmin_sms.settings import * # 短信服务
|
||||
# from dvadmin_third.settings import * # 扫码登录
|
||||
# from dvadmin_uniapp.settings import * # UniApp后端
|
||||
# from dvadmin_ak_sk.settings import * # 秘钥管理管理
|
||||
# from dvadmin_tenants.settings import * # 租户管理
|
||||
# ...
|
||||
from dvadmin_third.settings import * # 扫码登录
|
||||
from dvadmin_uniapp.settings import * # uniapp后端
|
||||
|
||||
# ********** 一键导入插件配置结束 **********
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
# python manage.py makemigrations
|
||||
# python manage.py migrate
|
||||
# python manage.py init -y
|
||||
gunicorn -c gunicorn.py application.asgi:application
|
||||
gunicorn -c gunicorn_conf.py application.asgi:application
|
||||
|
|
|
@ -61,6 +61,6 @@ def CustomExceptionHandler(ex, context):
|
|||
# set_rollback()
|
||||
# msg = "接口服务器异常,请联系管理员"
|
||||
elif isinstance(ex, Exception):
|
||||
logger.error(traceback.format_exc())
|
||||
logger.exception(traceback.format_exc())
|
||||
msg = str(ex)
|
||||
return ErrorResponse(msg=msg, code=code)
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
import logging
|
||||
import os.path
|
||||
from logging import LogRecord
|
||||
|
||||
from django.core.servers.basehttp import WSGIRequestHandler
|
||||
from loguru import logger
|
||||
|
||||
from logging.handlers import RotatingFileHandler
|
||||
|
||||
# 1.🎖️先声明一个类继承logging.Handler(制作一件品如的衣服)
|
||||
from loguru._defaults import LOGURU_FORMAT
|
||||
|
||||
|
||||
class InterceptTimedRotatingFileHandler(RotatingFileHandler):
|
||||
"""
|
||||
自定义反射时间回滚日志记录器
|
||||
缺少命名空间
|
||||
"""
|
||||
|
||||
def __init__(self, filename, when='d', interval=1, backupCount=5, encoding="utf-8", delay=False, utc=False,
|
||||
maxBytes=1024 * 1024 * 100, atTime=None, logging_levels="all"):
|
||||
super(InterceptTimedRotatingFileHandler, self).__init__(filename)
|
||||
filename = os.path.abspath(filename)
|
||||
when = when.lower()
|
||||
# 2.🎖️需要本地用不同的文件名做为不同日志的筛选器
|
||||
self.logger_ = logger.bind(sime=filename, ip="-", port="-", username="张三")
|
||||
self.filename = filename
|
||||
key_map = {
|
||||
'h': 'hour',
|
||||
'w': 'week',
|
||||
's': 'second',
|
||||
'm': 'minute',
|
||||
'd': 'day',
|
||||
}
|
||||
# 根据输入文件格式及时间回滚设立文件名称
|
||||
rotation = f"{maxBytes / 1024 / 1024}MB"
|
||||
retention = "%d %ss" % (backupCount, key_map[when])
|
||||
time_format = "{time:%Y-%m-%d_%H-%M-%S}"
|
||||
if when == "s":
|
||||
time_format = "{time:%Y-%m-%d_%H-%M-%S}"
|
||||
elif when == "m":
|
||||
time_format = "{time:%Y-%m-%d_%H-%M}"
|
||||
elif when == "h":
|
||||
time_format = "{time:%Y-%m-%d_%H}"
|
||||
elif when == "d":
|
||||
time_format = "{time:%Y-%m-%d}"
|
||||
elif when == "w":
|
||||
time_format = "{time:%Y-%m-%d}"
|
||||
level_keys = ["info"]
|
||||
# 3.🎖️构建一个筛选器
|
||||
levels = {
|
||||
"debug": lambda x: "DEBUG" == x['level'].name.upper() and x['extra'].get('sime') == filename,
|
||||
"error": lambda x: "ERROR" == x['level'].name.upper() and x['extra'].get('sime') == filename,
|
||||
"info": lambda x: "INFO" == x['level'].name.upper() and x['extra'].get('sime') == filename,
|
||||
"warning": lambda x: "WARNING" == x['level'].name.upper() and x['extra'].get('sime') == filename
|
||||
}
|
||||
# 4. 🎖️根据输出构建筛选器
|
||||
if isinstance(logging_levels, str):
|
||||
if logging_levels.lower() == "all":
|
||||
level_keys = levels.keys()
|
||||
elif logging_levels.lower() in levels:
|
||||
level_keys = [logging_levels]
|
||||
elif isinstance(logging_levels, (list, tuple)):
|
||||
level_keys = logging_levels
|
||||
for k, f in {_: levels[_] for _ in level_keys}.items():
|
||||
|
||||
# 5.🎖️为防止重复添加sink,而重复写入日志,需要判断是否已经装载了对应sink,防止其使用秘技:反复横跳。
|
||||
filename_fmt = filename.replace(".log", "_%s_%s.log" % (time_format, k))
|
||||
# noinspection PyUnresolvedReferences,PyProtectedMember
|
||||
file_key = {_._name: han_id for han_id, _ in self.logger_._core.handlers.items()}
|
||||
filename_fmt_key = "'{}'".format(filename_fmt)
|
||||
if filename_fmt_key in file_key:
|
||||
continue
|
||||
# self.logger_.remove(file_key[filename_fmt_key])
|
||||
self.logger_.add(
|
||||
filename_fmt,
|
||||
# format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <green>{extra[ip]}:{extra[port]}</green> | <level>{level: <8}</level>| <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
|
||||
retention=retention,
|
||||
encoding=encoding,
|
||||
level=self.level,
|
||||
rotation=rotation,
|
||||
compression="zip", # 日志归档自行压缩文件
|
||||
delay=delay,
|
||||
enqueue=True,
|
||||
backtrace=True,
|
||||
filter=f
|
||||
)
|
||||
|
||||
def emit(self, record):
|
||||
try:
|
||||
level = self.logger_.level(record.levelname).name
|
||||
except ValueError:
|
||||
level = record.levelno
|
||||
|
||||
frame, depth = logging.currentframe(), 2
|
||||
# 6.🎖️把当前帧的栈深度回到发生异常的堆栈深度,不然就是当前帧发生异常而无法回溯
|
||||
while frame.f_code.co_filename == logging.__file__:
|
||||
frame = frame.f_back
|
||||
depth += 1
|
||||
# 设置自定义属性
|
||||
port = "-"
|
||||
ip = "-"
|
||||
locals_self = frame.f_locals.get('self', None)
|
||||
msg = self.format(record)
|
||||
if locals_self and hasattr(locals_self, 'client_address'):
|
||||
ip, port = locals_self.client_address
|
||||
# - 127.0.0.1:56525 -
|
||||
msg = f"{ip}:{port} - {msg}"
|
||||
self.logger_ \
|
||||
.opt(depth=depth, exception=record.exc_info, colors=True) \
|
||||
.bind(ip=ip, port=port) \
|
||||
.log(level, msg)
|
|
@ -23,7 +23,7 @@ pidfile = './gunicorn.pid'
|
|||
# 日志级别,这个日志级别指的是错误日志的级别,而访问日志的级别无法设置
|
||||
loglevel = 'info'
|
||||
# 设置gunicorn访问日志格式,错误日志无法设置
|
||||
access_log_format = '%(t)s %(p)s %(h)s "%(r)s" %(s)s %(L)s %(b)s %(f)s" "%(a)s"'
|
||||
access_log_format = '' # worker_class 为 uvicorn.workers.UvicornWorker 时,日志格式为Django的loggers
|
||||
# 监听队列
|
||||
backlog = 512
|
||||
#进程名
|
|
@ -3,31 +3,31 @@ certifi==2021.5.30
|
|||
chardet==4.0.0
|
||||
coreapi==2.3.3
|
||||
coreschema==0.0.4
|
||||
Django==3.2.3
|
||||
django-comment-migrate==0.1.5
|
||||
Django==3.2.12
|
||||
django-comment-migrate==0.1.7
|
||||
django-cors-headers==3.10.1
|
||||
django-filter==21.1
|
||||
django-filter==22.1
|
||||
django-ranged-response==0.2.0
|
||||
django-restql==0.15.1
|
||||
django-simple-captcha==0.5.14
|
||||
django-restql==0.15.3
|
||||
django-simple-captcha==0.5.17
|
||||
django-timezone-field==4.2.3
|
||||
djangorestframework==3.12.4
|
||||
djangorestframework-simplejwt==5.1.0
|
||||
drf-yasg==1.20.0
|
||||
djangorestframework==3.14.0
|
||||
djangorestframework-simplejwt==5.2.2
|
||||
packaging==23.0
|
||||
drf-yasg==1.21.5
|
||||
idna==2.10
|
||||
inflection==0.5.1
|
||||
itypes==1.2.0
|
||||
Jinja2==3.0.1
|
||||
Jinja2==3.1.2
|
||||
MarkupSafe==2.0.1
|
||||
mysqlclient==2.0.3
|
||||
packaging==20.9
|
||||
Pillow==8.3.1
|
||||
PyJWT==2.1.0
|
||||
mysqlclient==2.1.1
|
||||
Pillow==9.4.0
|
||||
PyJWT==2.6.0
|
||||
pyparsing==2.4.7
|
||||
pyPEG2==2.15.2
|
||||
pypinyin==0.46.0
|
||||
pypinyin==0.48.0
|
||||
pytz==2021.1
|
||||
requests==2.25.1
|
||||
requests==2.28.2
|
||||
ruamel.yaml==0.17.10
|
||||
ruamel.yaml.clib==0.2.4
|
||||
six==1.16.0
|
||||
|
@ -37,13 +37,14 @@ typing-extensions==3.10.0.0
|
|||
tzlocal==2.1
|
||||
ua-parser==0.10.0
|
||||
uritemplate==3.0.1
|
||||
urllib3==1.26.6
|
||||
urllib3==1.26.15
|
||||
user-agents==2.2.0
|
||||
whitenoise==5.3.0
|
||||
openpyxl==3.0.9
|
||||
openpyxl==3.1.2
|
||||
channels==3.0.5
|
||||
channels-redis==3.4.1
|
||||
uvicorn==0.20.0
|
||||
uvicorn==0.21.1
|
||||
gunicorn==20.1.0
|
||||
gevent==22.10.2
|
||||
websockets==10.4
|
||||
loguru==0.6.0
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 326 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 121 KiB |
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<el-card shadow="never" header="阿里云" class="item-background">
|
||||
<img src="/image/card/ali.png" @click="Jump()" alt="阿里云" class="img-style">
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
title: '阿里云推广',
|
||||
icon: 'el-icon-present',
|
||||
description: '阿里云推广',
|
||||
height: 15,
|
||||
minH: 10,
|
||||
width: 14,
|
||||
minW: 10,
|
||||
isResizable: true,
|
||||
methods: {
|
||||
Jump () {
|
||||
window.open('https://www.aliyun.com/minisite/goods?userCode=jpef8a71&share_source=copy_link', '_black')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<el-card shadow="never" header="短信服务" class="item-background">
|
||||
<img src="/image/card/sms.png" @click="Jump()" alt="短信服务" class="img-style">
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
title: '短信服务',
|
||||
icon: 'el-icon-message',
|
||||
description: '点击跳转到短信服务',
|
||||
height: 10,
|
||||
minH: 10,
|
||||
width: 5,
|
||||
minW: 5,
|
||||
isResizable: true,
|
||||
methods: {
|
||||
Jump () {
|
||||
window.open('https://bbs.django-vue-admin.com/plugMarket/128.html', '_black')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<el-card shadow="never" header="腾讯云" class="item-background">
|
||||
<img src="/image/card/tencent.jpg" @click="Jump()" alt="腾讯云" class="img-style">
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
title: '腾讯云推广',
|
||||
icon: 'el-icon-medal',
|
||||
description: '腾讯云推广',
|
||||
height: 10,
|
||||
minH: 10,
|
||||
width: 5,
|
||||
isResizable: true,
|
||||
methods: {
|
||||
Jump () {
|
||||
window.open('https://cloud.tencent.com/act/cps/redirect?redirect=1060&cps_key=b302a514a6688aa30823fac954464e5d&from=console', '_black')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.img-style {
|
||||
cursor: pointer;
|
||||
height: 180px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue