功能变化: 添加loguru日志支持
							parent
							
								
									cde3e75381
								
							
						
					
					
						commit
						6d9e49897e
					
				|  | @ -190,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配置 *************** # | ||||
| # ================================================= # | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 李强
						李强