sanic-api 0.2.5__py3-none-any.whl → 0.2.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- example/__main__.py +19 -19
- example/api/__init__.py +1 -1
- example/api/t1/__init__.py +2 -1
- example/api/t1/{t1.py → t1_1.py} +22 -22
- example/api/t1/t1_2.py +9 -0
- example/api/t2/__init__.py +1 -1
- example/api/t2/t2.py +9 -9
- example/api/t2/t3/__init__.py +1 -1
- example/api/t2/t3/t3.py +9 -9
- example/api/user.py +89 -89
- example/api/validator.py +97 -97
- example/app.py +62 -62
- example/settings.py +10 -10
- sanic_api/__init__.py +21 -21
- sanic_api/api/__init__.py +2 -2
- sanic_api/api/api.py +177 -177
- sanic_api/api/exception.py +46 -46
- sanic_api/api/extend.py +25 -25
- sanic_api/api/handle_exception.py +61 -61
- sanic_api/api/model.py +45 -45
- sanic_api/api/validators.py +76 -76
- sanic_api/config/__init__.py +2 -2
- sanic_api/config/base.py +110 -110
- sanic_api/config/sanic.py +19 -21
- sanic_api/config/sanic_api.py +11 -13
- sanic_api/config/setting.py +30 -30
- sanic_api/enum.py +88 -88
- sanic_api/logger/__init__.py +1 -1
- sanic_api/logger/config.py +77 -77
- sanic_api/logger/extend.py +71 -71
- sanic_api/logger/sanic_http.py +68 -68
- sanic_api/openapi/__init__.py +7 -7
- sanic_api/openapi/openapi.py +102 -102
- sanic_api/utils.py +112 -97
- {sanic_api-0.2.5.dist-info → sanic_api-0.2.7.dist-info}/METADATA +4 -3
- sanic_api-0.2.7.dist-info/RECORD +39 -0
- sanic_api-0.2.7.dist-info/WHEEL +4 -0
- sanic_api-0.2.5.dist-info/RECORD +0 -38
- sanic_api-0.2.5.dist-info/WHEEL +0 -4
- {sanic_api-0.2.5.dist-info → sanic_api-0.2.7.dist-info}/licenses/LICENSE.txt +0 -0
sanic_api/enum.py
CHANGED
@@ -1,88 +1,88 @@
|
|
1
|
-
import json
|
2
|
-
from dataclasses import dataclass, field
|
3
|
-
from enum import Enum, auto
|
4
|
-
from types import DynamicClassAttribute
|
5
|
-
from typing import Any
|
6
|
-
|
7
|
-
|
8
|
-
@dataclass
|
9
|
-
class EnumField:
|
10
|
-
"""
|
11
|
-
枚举字段类
|
12
|
-
"""
|
13
|
-
|
14
|
-
value: Any = field(default=auto())
|
15
|
-
desc: str = field(default_factory=str)
|
16
|
-
|
17
|
-
|
18
|
-
class EnumBase(Enum):
|
19
|
-
"""
|
20
|
-
枚举基类
|
21
|
-
"""
|
22
|
-
|
23
|
-
@classmethod
|
24
|
-
def _missing_(cls, value: object) -> Any:
|
25
|
-
result = list(filter(lambda d: d.value == value, cls)) # type: ignore
|
26
|
-
return result[0] if result else None
|
27
|
-
|
28
|
-
@DynamicClassAttribute
|
29
|
-
def value(self) -> Any:
|
30
|
-
"""
|
31
|
-
获取枚举的值
|
32
|
-
Returns:
|
33
|
-
|
34
|
-
"""
|
35
|
-
if isinstance(self._value_, EnumField):
|
36
|
-
return self._value_.value
|
37
|
-
return self._value_
|
38
|
-
|
39
|
-
@DynamicClassAttribute
|
40
|
-
def desc(self) -> str:
|
41
|
-
"""
|
42
|
-
获取枚举值的描述
|
43
|
-
Returns:
|
44
|
-
|
45
|
-
"""
|
46
|
-
if isinstance(self._value_, EnumField):
|
47
|
-
return self._value_.desc
|
48
|
-
else:
|
49
|
-
return ""
|
50
|
-
|
51
|
-
@classmethod
|
52
|
-
def list(cls) -> list:
|
53
|
-
return [c.value for c in cls]
|
54
|
-
|
55
|
-
@classmethod
|
56
|
-
def to_desc(cls) -> str:
|
57
|
-
data = {d.value: d.desc for d in cls}
|
58
|
-
return json.dumps(data, ensure_ascii=False)
|
59
|
-
|
60
|
-
|
61
|
-
class RespCodeEnum(EnumBase):
|
62
|
-
"""
|
63
|
-
响应码枚举
|
64
|
-
"""
|
65
|
-
|
66
|
-
SUCCESS = EnumField(10000, desc="成功")
|
67
|
-
FAILED = EnumField(40000, desc="失败")
|
68
|
-
PARAM_FAILED = EnumField(40001, desc="参数校验失败")
|
69
|
-
|
70
|
-
|
71
|
-
class ParamEnum(EnumBase):
|
72
|
-
"""
|
73
|
-
参数位置
|
74
|
-
"""
|
75
|
-
|
76
|
-
JSON = EnumField("json")
|
77
|
-
FORM = EnumField("form")
|
78
|
-
QUERY = EnumField("query")
|
79
|
-
|
80
|
-
|
81
|
-
class RunModeEnum(EnumBase):
|
82
|
-
"""
|
83
|
-
运行模式
|
84
|
-
"""
|
85
|
-
|
86
|
-
DEV = EnumField("dev", desc="开发模式。相当于debug模式加自动重载")
|
87
|
-
DEBUG = EnumField("debug", desc="调试模式。单workers启动。输出将更加详细,并将禁用多个运行时优化")
|
88
|
-
PRODUCTION = EnumField("production", desc="生产模式。如未指定workers数量,则自动使用fast模式启动。")
|
1
|
+
import json
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from enum import Enum, auto
|
4
|
+
from types import DynamicClassAttribute
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class EnumField:
|
10
|
+
"""
|
11
|
+
枚举字段类
|
12
|
+
"""
|
13
|
+
|
14
|
+
value: Any = field(default=auto())
|
15
|
+
desc: str = field(default_factory=str)
|
16
|
+
|
17
|
+
|
18
|
+
class EnumBase(Enum):
|
19
|
+
"""
|
20
|
+
枚举基类
|
21
|
+
"""
|
22
|
+
|
23
|
+
@classmethod
|
24
|
+
def _missing_(cls, value: object) -> Any:
|
25
|
+
result = list(filter(lambda d: d.value == value, cls)) # type: ignore
|
26
|
+
return result[0] if result else None
|
27
|
+
|
28
|
+
@DynamicClassAttribute
|
29
|
+
def value(self) -> Any:
|
30
|
+
"""
|
31
|
+
获取枚举的值
|
32
|
+
Returns:
|
33
|
+
|
34
|
+
"""
|
35
|
+
if isinstance(self._value_, EnumField):
|
36
|
+
return self._value_.value
|
37
|
+
return self._value_
|
38
|
+
|
39
|
+
@DynamicClassAttribute
|
40
|
+
def desc(self) -> str:
|
41
|
+
"""
|
42
|
+
获取枚举值的描述
|
43
|
+
Returns:
|
44
|
+
|
45
|
+
"""
|
46
|
+
if isinstance(self._value_, EnumField):
|
47
|
+
return self._value_.desc
|
48
|
+
else:
|
49
|
+
return ""
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def list(cls) -> list:
|
53
|
+
return [c.value for c in cls]
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def to_desc(cls) -> str:
|
57
|
+
data = {d.value: d.desc for d in cls}
|
58
|
+
return json.dumps(data, ensure_ascii=False)
|
59
|
+
|
60
|
+
|
61
|
+
class RespCodeEnum(EnumBase):
|
62
|
+
"""
|
63
|
+
响应码枚举
|
64
|
+
"""
|
65
|
+
|
66
|
+
SUCCESS = EnumField(10000, desc="成功")
|
67
|
+
FAILED = EnumField(40000, desc="失败")
|
68
|
+
PARAM_FAILED = EnumField(40001, desc="参数校验失败")
|
69
|
+
|
70
|
+
|
71
|
+
class ParamEnum(EnumBase):
|
72
|
+
"""
|
73
|
+
参数位置
|
74
|
+
"""
|
75
|
+
|
76
|
+
JSON = EnumField("json")
|
77
|
+
FORM = EnumField("form")
|
78
|
+
QUERY = EnumField("query")
|
79
|
+
|
80
|
+
|
81
|
+
class RunModeEnum(EnumBase):
|
82
|
+
"""
|
83
|
+
运行模式
|
84
|
+
"""
|
85
|
+
|
86
|
+
DEV = EnumField("dev", desc="开发模式。相当于debug模式加自动重载")
|
87
|
+
DEBUG = EnumField("debug", desc="调试模式。单workers启动。输出将更加详细,并将禁用多个运行时优化")
|
88
|
+
PRODUCTION = EnumField("production", desc="生产模式。如未指定workers数量,则自动使用fast模式启动。")
|
sanic_api/logger/__init__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
from .extend import LoggerExtend
|
1
|
+
from .extend import LoggerExtend
|
sanic_api/logger/config.py
CHANGED
@@ -1,77 +1,77 @@
|
|
1
|
-
import logging
|
2
|
-
import logging.config
|
3
|
-
import sys
|
4
|
-
from types import FrameType
|
5
|
-
from typing import Optional, Union
|
6
|
-
|
7
|
-
import sanic.exceptions
|
8
|
-
from loguru import logger
|
9
|
-
from pygments import highlight
|
10
|
-
from pygments.formatters.terminal import TerminalFormatter
|
11
|
-
from pygments.lexers.sql import PostgresLexer
|
12
|
-
from sanic import Request
|
13
|
-
|
14
|
-
|
15
|
-
class InterceptHandler(logging.StreamHandler):
|
16
|
-
def emit(self, record: logging.LogRecord):
|
17
|
-
# Get corresponding Loguru level if it exists
|
18
|
-
try:
|
19
|
-
level: Union[int, str] = logger.level(record.levelname).name
|
20
|
-
except ValueError:
|
21
|
-
level = record.levelno
|
22
|
-
|
23
|
-
# Find caller from where originated the logged message.
|
24
|
-
# noinspection PyProtectedMember,PyUnresolvedReferences
|
25
|
-
frame: Optional[FrameType] = sys._getframe(6)
|
26
|
-
depth: int = 6
|
27
|
-
while frame and frame.f_code.co_filename == logging.__file__:
|
28
|
-
frame = frame.f_back
|
29
|
-
depth += 1
|
30
|
-
|
31
|
-
fmt = "%(message)s"
|
32
|
-
sanic_access_fmt = "[%(host)s]: %(request)s %(message)s %(status)d %(byte)s %(time)s%(req_args)s"
|
33
|
-
fmt = fmt if record.name != "sanic.access" else sanic_access_fmt
|
34
|
-
formatter = logging.Formatter(fmt=fmt)
|
35
|
-
msg = formatter.format(record)
|
36
|
-
msg = self.highlight_sql(record, msg)
|
37
|
-
req_id = self.get_req_id()
|
38
|
-
|
39
|
-
if "Dispatching signal" not in msg:
|
40
|
-
etxra_data = {"type": record.name, "req_id": req_id}
|
41
|
-
logger.bind(**etxra_data).opt(depth=depth, exception=record.exc_info).log(level, msg)
|
42
|
-
|
43
|
-
@staticmethod
|
44
|
-
def get_req_id():
|
45
|
-
"""
|
46
|
-
获取请求ID
|
47
|
-
"""
|
48
|
-
try:
|
49
|
-
req = Request.get_current()
|
50
|
-
req_id = f" [{req.id}] "
|
51
|
-
except sanic.exceptions.ServerError:
|
52
|
-
req_id = " "
|
53
|
-
return req_id
|
54
|
-
|
55
|
-
def highlight_sql(self, record: logging.LogRecord, message: str):
|
56
|
-
"""
|
57
|
-
打印日志时高亮SQl
|
58
|
-
Args:
|
59
|
-
record: 日志记录
|
60
|
-
message: 日志消息
|
61
|
-
|
62
|
-
Returns:
|
63
|
-
|
64
|
-
"""
|
65
|
-
name = record.name
|
66
|
-
postgres = PostgresLexer()
|
67
|
-
terminal_formatter = TerminalFormatter()
|
68
|
-
|
69
|
-
if name == "tortoise.db_client":
|
70
|
-
if (
|
71
|
-
record.levelname == "DEBUG"
|
72
|
-
and not message.startswith("Created connection pool")
|
73
|
-
and not message.startswith("Closed connection pool")
|
74
|
-
):
|
75
|
-
message = highlight(message, postgres, terminal_formatter).rstrip()
|
76
|
-
|
77
|
-
return message
|
1
|
+
import logging
|
2
|
+
import logging.config
|
3
|
+
import sys
|
4
|
+
from types import FrameType
|
5
|
+
from typing import Optional, Union
|
6
|
+
|
7
|
+
import sanic.exceptions
|
8
|
+
from loguru import logger
|
9
|
+
from pygments import highlight
|
10
|
+
from pygments.formatters.terminal import TerminalFormatter
|
11
|
+
from pygments.lexers.sql import PostgresLexer
|
12
|
+
from sanic import Request
|
13
|
+
|
14
|
+
|
15
|
+
class InterceptHandler(logging.StreamHandler):
|
16
|
+
def emit(self, record: logging.LogRecord):
|
17
|
+
# Get corresponding Loguru level if it exists
|
18
|
+
try:
|
19
|
+
level: Union[int, str] = logger.level(record.levelname).name
|
20
|
+
except ValueError:
|
21
|
+
level = record.levelno
|
22
|
+
|
23
|
+
# Find caller from where originated the logged message.
|
24
|
+
# noinspection PyProtectedMember,PyUnresolvedReferences
|
25
|
+
frame: Optional[FrameType] = sys._getframe(6)
|
26
|
+
depth: int = 6
|
27
|
+
while frame and frame.f_code.co_filename == logging.__file__:
|
28
|
+
frame = frame.f_back
|
29
|
+
depth += 1
|
30
|
+
|
31
|
+
fmt = "%(message)s"
|
32
|
+
sanic_access_fmt = "[%(host)s]: %(request)s %(message)s %(status)d %(byte)s %(time)s%(req_args)s"
|
33
|
+
fmt = fmt if record.name != "sanic.access" else sanic_access_fmt
|
34
|
+
formatter = logging.Formatter(fmt=fmt)
|
35
|
+
msg = formatter.format(record)
|
36
|
+
msg = self.highlight_sql(record, msg)
|
37
|
+
req_id = self.get_req_id()
|
38
|
+
|
39
|
+
if "Dispatching signal" not in msg:
|
40
|
+
etxra_data = {"type": record.name, "req_id": req_id}
|
41
|
+
logger.bind(**etxra_data).opt(depth=depth, exception=record.exc_info).log(level, msg)
|
42
|
+
|
43
|
+
@staticmethod
|
44
|
+
def get_req_id():
|
45
|
+
"""
|
46
|
+
获取请求ID
|
47
|
+
"""
|
48
|
+
try:
|
49
|
+
req = Request.get_current()
|
50
|
+
req_id = f" [{req.id}] "
|
51
|
+
except sanic.exceptions.ServerError:
|
52
|
+
req_id = " "
|
53
|
+
return req_id
|
54
|
+
|
55
|
+
def highlight_sql(self, record: logging.LogRecord, message: str):
|
56
|
+
"""
|
57
|
+
打印日志时高亮SQl
|
58
|
+
Args:
|
59
|
+
record: 日志记录
|
60
|
+
message: 日志消息
|
61
|
+
|
62
|
+
Returns:
|
63
|
+
|
64
|
+
"""
|
65
|
+
name = record.name
|
66
|
+
postgres = PostgresLexer()
|
67
|
+
terminal_formatter = TerminalFormatter()
|
68
|
+
|
69
|
+
if name == "tortoise.db_client":
|
70
|
+
if (
|
71
|
+
record.levelname == "DEBUG"
|
72
|
+
and not message.startswith("Created connection pool")
|
73
|
+
and not message.startswith("Closed connection pool")
|
74
|
+
):
|
75
|
+
message = highlight(message, postgres, terminal_formatter).rstrip()
|
76
|
+
|
77
|
+
return message
|
sanic_api/logger/extend.py
CHANGED
@@ -1,71 +1,71 @@
|
|
1
|
-
import logging
|
2
|
-
import logging.config
|
3
|
-
import sys
|
4
|
-
import time
|
5
|
-
|
6
|
-
from loguru import logger
|
7
|
-
|
8
|
-
# noinspection PyProtectedMember
|
9
|
-
from loguru._defaults import env
|
10
|
-
from sanic import HTTPResponse, Request
|
11
|
-
from sanic.application.constants import Mode
|
12
|
-
from sanic.server import HttpProtocol
|
13
|
-
from sanic_ext import Extension
|
14
|
-
|
15
|
-
from sanic_api.logger.config import InterceptHandler
|
16
|
-
from sanic_api.logger.sanic_http import SanicHttp
|
17
|
-
|
18
|
-
|
19
|
-
class LoggerExtend(Extension):
|
20
|
-
"""
|
21
|
-
处理日志的扩展
|
22
|
-
"""
|
23
|
-
|
24
|
-
name = "LoggerExtend"
|
25
|
-
|
26
|
-
def startup(self, bootstrap) -> None:
|
27
|
-
if not self.included():
|
28
|
-
return
|
29
|
-
|
30
|
-
log_level = logging.DEBUG if self.app.state.mode is Mode.DEBUG else logging.INFO
|
31
|
-
log_format = env(
|
32
|
-
"LOGURU_FORMAT",
|
33
|
-
str,
|
34
|
-
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
35
|
-
"<red>{extra[type]: <10}</red> | "
|
36
|
-
"<level>{level: <8}</level> | "
|
37
|
-
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - {extra[req_id]}<level>{message}</level>",
|
38
|
-
)
|
39
|
-
logger.remove()
|
40
|
-
logger.add(sys.stdout, colorize=True, format=log_format)
|
41
|
-
|
42
|
-
logging.basicConfig(handlers=[InterceptHandler()], level=log_level, force=True)
|
43
|
-
|
44
|
-
HttpProtocol.HTTP_CLASS = SanicHttp
|
45
|
-
self.app.on_request(self.proc_request, priority=999)
|
46
|
-
self.app.on_response(self.proc_response, priority=0)
|
47
|
-
|
48
|
-
async def proc_request(self, request: Request):
|
49
|
-
"""
|
50
|
-
处理请求的中间件
|
51
|
-
Args:
|
52
|
-
request:
|
53
|
-
|
54
|
-
Returns:
|
55
|
-
|
56
|
-
"""
|
57
|
-
request.ctx.st = time.perf_counter()
|
58
|
-
|
59
|
-
async def proc_response(self, request: Request, response: HTTPResponse):
|
60
|
-
"""
|
61
|
-
处理响应的中间件
|
62
|
-
Args:
|
63
|
-
request: 请求响应
|
64
|
-
response: 响应
|
65
|
-
|
66
|
-
Returns:
|
67
|
-
|
68
|
-
"""
|
69
|
-
request.ctx.et = time.perf_counter()
|
70
|
-
|
71
|
-
return response
|
1
|
+
import logging
|
2
|
+
import logging.config
|
3
|
+
import sys
|
4
|
+
import time
|
5
|
+
|
6
|
+
from loguru import logger
|
7
|
+
|
8
|
+
# noinspection PyProtectedMember
|
9
|
+
from loguru._defaults import env
|
10
|
+
from sanic import HTTPResponse, Request
|
11
|
+
from sanic.application.constants import Mode
|
12
|
+
from sanic.server import HttpProtocol
|
13
|
+
from sanic_ext import Extension
|
14
|
+
|
15
|
+
from sanic_api.logger.config import InterceptHandler
|
16
|
+
from sanic_api.logger.sanic_http import SanicHttp
|
17
|
+
|
18
|
+
|
19
|
+
class LoggerExtend(Extension):
|
20
|
+
"""
|
21
|
+
处理日志的扩展
|
22
|
+
"""
|
23
|
+
|
24
|
+
name = "LoggerExtend"
|
25
|
+
|
26
|
+
def startup(self, bootstrap) -> None:
|
27
|
+
if not self.included():
|
28
|
+
return
|
29
|
+
|
30
|
+
log_level = logging.DEBUG if self.app.state.mode is Mode.DEBUG else logging.INFO
|
31
|
+
log_format = env(
|
32
|
+
"LOGURU_FORMAT",
|
33
|
+
str,
|
34
|
+
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
|
35
|
+
"<red>{extra[type]: <10}</red> | "
|
36
|
+
"<level>{level: <8}</level> | "
|
37
|
+
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - {extra[req_id]}<level>{message}</level>",
|
38
|
+
)
|
39
|
+
logger.remove()
|
40
|
+
logger.add(sys.stdout, colorize=True, format=log_format)
|
41
|
+
|
42
|
+
logging.basicConfig(handlers=[InterceptHandler()], level=log_level, force=True)
|
43
|
+
|
44
|
+
HttpProtocol.HTTP_CLASS = SanicHttp
|
45
|
+
self.app.on_request(self.proc_request, priority=999)
|
46
|
+
self.app.on_response(self.proc_response, priority=0)
|
47
|
+
|
48
|
+
async def proc_request(self, request: Request):
|
49
|
+
"""
|
50
|
+
处理请求的中间件
|
51
|
+
Args:
|
52
|
+
request:
|
53
|
+
|
54
|
+
Returns:
|
55
|
+
|
56
|
+
"""
|
57
|
+
request.ctx.st = time.perf_counter()
|
58
|
+
|
59
|
+
async def proc_response(self, request: Request, response: HTTPResponse):
|
60
|
+
"""
|
61
|
+
处理响应的中间件
|
62
|
+
Args:
|
63
|
+
request: 请求响应
|
64
|
+
response: 响应
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
|
68
|
+
"""
|
69
|
+
request.ctx.et = time.perf_counter()
|
70
|
+
|
71
|
+
return response
|
sanic_api/logger/sanic_http.py
CHANGED
@@ -1,68 +1,68 @@
|
|
1
|
-
from sanic import Request
|
2
|
-
from sanic.http import Http
|
3
|
-
from sanic.log import access_logger
|
4
|
-
|
5
|
-
from sanic_api.utils import json_dumps
|
6
|
-
|
7
|
-
|
8
|
-
class SanicHttp(Http):
|
9
|
-
def log_response(self) -> None:
|
10
|
-
"""
|
11
|
-
自定义输出访问日志
|
12
|
-
Returns:
|
13
|
-
|
14
|
-
"""
|
15
|
-
req, res = self.request, self.response
|
16
|
-
|
17
|
-
dt = (req.ctx.et - req.ctx.st) * 1000
|
18
|
-
size = getattr(self, "response_bytes_left", getattr(self, "response_size", -1))
|
19
|
-
req_args = self.get_req_args(req)
|
20
|
-
extra = {
|
21
|
-
"status": getattr(res, "status", 0),
|
22
|
-
"byte": self.format_size(size),
|
23
|
-
"host": "UNKNOWN",
|
24
|
-
"request": "nil",
|
25
|
-
"time": f"{dt:.4f} ms",
|
26
|
-
"req_args": f" args: {req_args}" if req_args else "",
|
27
|
-
}
|
28
|
-
if req is not None:
|
29
|
-
if req.remote_addr or req.ip:
|
30
|
-
extra["host"] = f"{req.remote_addr or req.ip}:{req.port}"
|
31
|
-
extra["request"] = f"{req.method} {req.url}"
|
32
|
-
access_logger.info("", extra=extra)
|
33
|
-
|
34
|
-
def format_size(self, size: float):
|
35
|
-
"""
|
36
|
-
格式化输出大小
|
37
|
-
:param size: 大小
|
38
|
-
:return: 返回格式化的字符串
|
39
|
-
"""
|
40
|
-
for count in ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
|
41
|
-
if -1024.0 < size < 1024.0:
|
42
|
-
return f"{size:3.1f} {count}"
|
43
|
-
size /= 1024.0
|
44
|
-
return f"{size:3.1f} YB"
|
45
|
-
|
46
|
-
def get_req_args(self, request: Request) -> str:
|
47
|
-
"""
|
48
|
-
获取请求参数
|
49
|
-
Args:
|
50
|
-
request: 请求
|
51
|
-
|
52
|
-
Returns:
|
53
|
-
返回具有 json、query、form参数的json
|
54
|
-
"""
|
55
|
-
data = {}
|
56
|
-
for attr in ["args", "form"]:
|
57
|
-
attr_data = {}
|
58
|
-
for k, v in getattr(request, attr).items():
|
59
|
-
if type(v) == list and len(v) == 1:
|
60
|
-
attr_data[k] = v[0]
|
61
|
-
else:
|
62
|
-
attr_data[k] = v
|
63
|
-
if attr_data:
|
64
|
-
data[attr] = attr_data
|
65
|
-
if request.json:
|
66
|
-
data["json"] = request.json
|
67
|
-
|
68
|
-
return json_dumps(data) if data else ""
|
1
|
+
from sanic import Request
|
2
|
+
from sanic.http import Http
|
3
|
+
from sanic.log import access_logger
|
4
|
+
|
5
|
+
from sanic_api.utils import json_dumps
|
6
|
+
|
7
|
+
|
8
|
+
class SanicHttp(Http):
|
9
|
+
def log_response(self) -> None:
|
10
|
+
"""
|
11
|
+
自定义输出访问日志
|
12
|
+
Returns:
|
13
|
+
|
14
|
+
"""
|
15
|
+
req, res = self.request, self.response
|
16
|
+
|
17
|
+
dt = (req.ctx.et - req.ctx.st) * 1000
|
18
|
+
size = getattr(self, "response_bytes_left", getattr(self, "response_size", -1))
|
19
|
+
req_args = self.get_req_args(req)
|
20
|
+
extra = {
|
21
|
+
"status": getattr(res, "status", 0),
|
22
|
+
"byte": self.format_size(size),
|
23
|
+
"host": "UNKNOWN",
|
24
|
+
"request": "nil",
|
25
|
+
"time": f"{dt:.4f} ms",
|
26
|
+
"req_args": f" args: {req_args}" if req_args else "",
|
27
|
+
}
|
28
|
+
if req is not None:
|
29
|
+
if req.remote_addr or req.ip:
|
30
|
+
extra["host"] = f"{req.remote_addr or req.ip}:{req.port}"
|
31
|
+
extra["request"] = f"{req.method} {req.url}"
|
32
|
+
access_logger.info("", extra=extra)
|
33
|
+
|
34
|
+
def format_size(self, size: float):
|
35
|
+
"""
|
36
|
+
格式化输出大小
|
37
|
+
:param size: 大小
|
38
|
+
:return: 返回格式化的字符串
|
39
|
+
"""
|
40
|
+
for count in ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
|
41
|
+
if -1024.0 < size < 1024.0:
|
42
|
+
return f"{size:3.1f} {count}"
|
43
|
+
size /= 1024.0
|
44
|
+
return f"{size:3.1f} YB"
|
45
|
+
|
46
|
+
def get_req_args(self, request: Request) -> str:
|
47
|
+
"""
|
48
|
+
获取请求参数
|
49
|
+
Args:
|
50
|
+
request: 请求
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
返回具有 json、query、form参数的json
|
54
|
+
"""
|
55
|
+
data = {}
|
56
|
+
for attr in ["args", "form"]:
|
57
|
+
attr_data = {}
|
58
|
+
for k, v in getattr(request, attr).items():
|
59
|
+
if type(v) == list and len(v) == 1:
|
60
|
+
attr_data[k] = v[0]
|
61
|
+
else:
|
62
|
+
attr_data[k] = v
|
63
|
+
if attr_data:
|
64
|
+
data[attr] = attr_data
|
65
|
+
if request.json:
|
66
|
+
data["json"] = request.json
|
67
|
+
|
68
|
+
return json_dumps(data) if data else ""
|
sanic_api/openapi/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
from sanic import Sanic
|
2
|
-
|
3
|
-
from sanic_api.openapi.openapi import auto_doc
|
4
|
-
|
5
|
-
|
6
|
-
def init(sanic_app: Sanic):
|
7
|
-
sanic_app.before_server_start(auto_doc)
|
1
|
+
from sanic import Sanic
|
2
|
+
|
3
|
+
from sanic_api.openapi.openapi import auto_doc
|
4
|
+
|
5
|
+
|
6
|
+
def init(sanic_app: Sanic):
|
7
|
+
sanic_app.before_server_start(auto_doc)
|