diff --git a/backend/application/settings.py b/backend/application/settings.py index 77a0d2b..6d94b8d 100644 --- a/backend/application/settings.py +++ b/backend/application/settings.py @@ -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 = {} - - # ================================================= # # ******************** 插件配置 ******************** # # ================================================= # @@ -401,10 +386,13 @@ TENANT_SHARED_APPS = [] PLUGINS_URL_PATTERNS = [] # ********** 一键导入插件配置开始 ********** # 例如: -# from dvadmin_upgrade_center.settings import * # 升级中心 -# from dvadmin_celery.settings import * # celery 异步任务 +# 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后端 # ********** 一键导入插件配置结束 ********** diff --git a/backend/docker_start.sh b/backend/docker_start.sh index 6042fe6..1285d98 100755 --- a/backend/docker_start.sh +++ b/backend/docker_start.sh @@ -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 diff --git a/backend/dvadmin/utils/exception.py b/backend/dvadmin/utils/exception.py index 2335b4c..b03a26c 100644 --- a/backend/dvadmin/utils/exception.py +++ b/backend/dvadmin/utils/exception.py @@ -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) diff --git a/backend/dvadmin/utils/log.py b/backend/dvadmin/utils/log.py new file mode 100644 index 0000000..e8081f4 --- /dev/null +++ b/backend/dvadmin/utils/log.py @@ -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="{time:YYYY-MM-DD HH:mm:ss.SSS} | {extra[ip]}:{extra[port]} | {level: <8}| {name}:{function}:{line} - {message}", + 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) diff --git a/backend/gunicorn.py b/backend/gunicorn_conf.py similarity index 95% rename from backend/gunicorn.py rename to backend/gunicorn_conf.py index 1772fbc..ca2fb32 100644 --- a/backend/gunicorn.py +++ b/backend/gunicorn_conf.py @@ -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 #进程名 diff --git a/backend/requirements.txt b/backend/requirements.txt index fb07b24..c8660ac 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -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 diff --git a/web/public/image/card/ali.png b/web/public/image/card/ali.png new file mode 100644 index 0000000..874e6f9 Binary files /dev/null and b/web/public/image/card/ali.png differ diff --git a/web/public/image/card/sms.png b/web/public/image/card/sms.png new file mode 100644 index 0000000..9c70598 Binary files /dev/null and b/web/public/image/card/sms.png differ diff --git a/web/public/image/card/tencent.jpg b/web/public/image/card/tencent.jpg new file mode 100644 index 0000000..e37100b Binary files /dev/null and b/web/public/image/card/tencent.jpg differ diff --git a/web/src/views/dashboard/workbench/components/alicloud.vue b/web/src/views/dashboard/workbench/components/alicloud.vue new file mode 100644 index 0000000..a400b71 --- /dev/null +++ b/web/src/views/dashboard/workbench/components/alicloud.vue @@ -0,0 +1,23 @@ + + + diff --git a/web/src/views/dashboard/workbench/components/sms.vue b/web/src/views/dashboard/workbench/components/sms.vue new file mode 100644 index 0000000..3dfebff --- /dev/null +++ b/web/src/views/dashboard/workbench/components/sms.vue @@ -0,0 +1,23 @@ + + + diff --git a/web/src/views/dashboard/workbench/components/tencent.vue b/web/src/views/dashboard/workbench/components/tencent.vue new file mode 100644 index 0000000..1bebbed --- /dev/null +++ b/web/src/views/dashboard/workbench/components/tencent.vue @@ -0,0 +1,31 @@ + + + + +