toms-fast 0.2.1__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.
- toms_fast-0.2.1.dist-info/METADATA +467 -0
- toms_fast-0.2.1.dist-info/RECORD +60 -0
- toms_fast-0.2.1.dist-info/WHEEL +4 -0
- toms_fast-0.2.1.dist-info/entry_points.txt +2 -0
- tomskit/__init__.py +0 -0
- tomskit/celery/README.md +693 -0
- tomskit/celery/__init__.py +4 -0
- tomskit/celery/celery.py +306 -0
- tomskit/celery/config.py +377 -0
- tomskit/cli/__init__.py +207 -0
- tomskit/cli/__main__.py +8 -0
- tomskit/cli/scaffold.py +123 -0
- tomskit/cli/templates/__init__.py +42 -0
- tomskit/cli/templates/base.py +348 -0
- tomskit/cli/templates/celery.py +101 -0
- tomskit/cli/templates/extensions.py +213 -0
- tomskit/cli/templates/fastapi.py +400 -0
- tomskit/cli/templates/migrations.py +281 -0
- tomskit/cli/templates_config.py +122 -0
- tomskit/logger/README.md +466 -0
- tomskit/logger/__init__.py +4 -0
- tomskit/logger/config.py +106 -0
- tomskit/logger/logger.py +290 -0
- tomskit/py.typed +0 -0
- tomskit/redis/README.md +462 -0
- tomskit/redis/__init__.py +6 -0
- tomskit/redis/config.py +85 -0
- tomskit/redis/redis_pool.py +87 -0
- tomskit/redis/redis_sync.py +66 -0
- tomskit/server/__init__.py +47 -0
- tomskit/server/config.py +117 -0
- tomskit/server/exceptions.py +412 -0
- tomskit/server/middleware.py +371 -0
- tomskit/server/parser.py +312 -0
- tomskit/server/resource.py +464 -0
- tomskit/server/server.py +276 -0
- tomskit/server/type.py +263 -0
- tomskit/sqlalchemy/README.md +590 -0
- tomskit/sqlalchemy/__init__.py +20 -0
- tomskit/sqlalchemy/config.py +125 -0
- tomskit/sqlalchemy/database.py +125 -0
- tomskit/sqlalchemy/pagination.py +359 -0
- tomskit/sqlalchemy/property.py +19 -0
- tomskit/sqlalchemy/sqlalchemy.py +131 -0
- tomskit/sqlalchemy/types.py +32 -0
- tomskit/task/README.md +67 -0
- tomskit/task/__init__.py +4 -0
- tomskit/task/task_manager.py +124 -0
- tomskit/tools/README.md +63 -0
- tomskit/tools/__init__.py +18 -0
- tomskit/tools/config.py +70 -0
- tomskit/tools/warnings.py +37 -0
- tomskit/tools/woker.py +81 -0
- tomskit/utils/README.md +666 -0
- tomskit/utils/README_SERIALIZER.md +644 -0
- tomskit/utils/__init__.py +35 -0
- tomskit/utils/fields.py +434 -0
- tomskit/utils/marshal_utils.py +137 -0
- tomskit/utils/response_utils.py +13 -0
- tomskit/utils/serializers.py +447 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""
|
|
2
|
+
脚手架模板配置文件
|
|
3
|
+
定义项目目录结构和文件模板
|
|
4
|
+
|
|
5
|
+
注意:模板内容已迁移到 templates/ 目录下的模块化文件中:
|
|
6
|
+
- templates/base.py - 基础模板
|
|
7
|
+
- templates/extensions.py - 扩展模板
|
|
8
|
+
- templates/fastapi.py - FastAPI 模板
|
|
9
|
+
- templates/celery.py - Celery 模板
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from typing import Callable
|
|
13
|
+
|
|
14
|
+
from .templates import get_all_templates
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# 基础目录结构(所有类型都需要)
|
|
18
|
+
BASE_DIRECTORIES = {
|
|
19
|
+
"extensions": True, # 扩展功能初始化目录
|
|
20
|
+
"tests": True,
|
|
21
|
+
"logs": False,
|
|
22
|
+
"run": False,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
# FastAPI 相关目录
|
|
26
|
+
FASTAPI_DIRECTORIES = {
|
|
27
|
+
"app/controllers": True, # controllers 目录(__init__.py 为空)
|
|
28
|
+
"app/controllers/users": True,
|
|
29
|
+
"app/middleware": True, # 中间件目录
|
|
30
|
+
"app/models": True,
|
|
31
|
+
"app/schemas": True,
|
|
32
|
+
"app/services": True,
|
|
33
|
+
"app/utils": True,
|
|
34
|
+
"migrations": True, # 数据库迁移目录
|
|
35
|
+
"migrations/versions": True, # 迁移版本目录
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# Celery 相关目录
|
|
39
|
+
CELERY_DIRECTORIES = {
|
|
40
|
+
"tasks": True,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_directory_structure(project_type: str) -> dict[str, bool]:
|
|
45
|
+
"""根据项目类型返回目录结构"""
|
|
46
|
+
directories = BASE_DIRECTORIES.copy()
|
|
47
|
+
|
|
48
|
+
if project_type in ("fastapi", "full"):
|
|
49
|
+
directories.update(FASTAPI_DIRECTORIES)
|
|
50
|
+
|
|
51
|
+
if project_type in ("celery", "full"):
|
|
52
|
+
directories.update(CELERY_DIRECTORIES)
|
|
53
|
+
|
|
54
|
+
return directories
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# 基础文件模板(所有类型都需要)
|
|
58
|
+
BASE_FILES = {
|
|
59
|
+
".env.example": "env_example",
|
|
60
|
+
".gitignore": "gitignore",
|
|
61
|
+
"pyproject.toml": "pyproject_toml",
|
|
62
|
+
"README.md": "readme_md",
|
|
63
|
+
"extensions/__init__.py": "extensions_init_py",
|
|
64
|
+
"extensions/ext_logger.py": "extensions_logger_py",
|
|
65
|
+
"tests/__init__.py": "tests_init_py",
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# FastAPI 相关文件
|
|
69
|
+
FASTAPI_FILES = {
|
|
70
|
+
"main.py": "main_py",
|
|
71
|
+
"app/__init__.py": "app_init_py",
|
|
72
|
+
"app/config.py": "config_py",
|
|
73
|
+
"app/middleware/__init__.py": "middleware_init_py",
|
|
74
|
+
"app/middleware/request_id.py": "middleware_request_id_py",
|
|
75
|
+
"app/middleware/resource_cleanup.py": "middleware_resource_cleanup_py",
|
|
76
|
+
"extensions/ext_database.py": "extensions_database_py",
|
|
77
|
+
"extensions/ext_redis.py": "extensions_redis_py",
|
|
78
|
+
"app/controllers/__init__.py": "controllers_init_py",
|
|
79
|
+
"app/controllers/users/__init__.py": "users_init_py",
|
|
80
|
+
"app/controllers/users/resources.py": "users_resources_py",
|
|
81
|
+
"app/controllers/users/schemas.py": "users_schemas_py",
|
|
82
|
+
"app/controllers/users/module.py": "users_module_py",
|
|
83
|
+
"app/models/__init__.py": "models_init_py",
|
|
84
|
+
"app/models/user.py": "user_model_py",
|
|
85
|
+
"tests/test_users.py": "test_users_py",
|
|
86
|
+
"migrations/__init__.py": "migrations_init_py",
|
|
87
|
+
"migrations/alembic.ini": "alembic_ini",
|
|
88
|
+
"migrations/env.py": "migrations_env_py",
|
|
89
|
+
"migrations/script.py.mako": "migrations_script_py_mako",
|
|
90
|
+
"migrations/versions/__init__.py": "migrations_versions_init_py",
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# Celery 相关文件
|
|
94
|
+
CELERY_FILES = {
|
|
95
|
+
"celery_app.py": "celery_app_py",
|
|
96
|
+
"extensions/ext_celery.py": "extensions_celery_py",
|
|
97
|
+
"tasks/__init__.py": "tasks_init_py",
|
|
98
|
+
"tasks/example_task.py": "example_task_py",
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def get_file_templates(project_type: str) -> dict[str, str]:
|
|
103
|
+
"""根据项目类型返回文件模板映射"""
|
|
104
|
+
files = BASE_FILES.copy()
|
|
105
|
+
|
|
106
|
+
if project_type in ("fastapi", "full"):
|
|
107
|
+
files.update(FASTAPI_FILES)
|
|
108
|
+
|
|
109
|
+
if project_type in ("celery", "full"):
|
|
110
|
+
files.update(CELERY_FILES)
|
|
111
|
+
|
|
112
|
+
return files
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# 文件模板内容生成函数
|
|
116
|
+
def get_template_functions(project_name: str, project_type: str = "full") -> dict[str, Callable[[], str]]:
|
|
117
|
+
"""
|
|
118
|
+
根据项目类型返回所有模板函数
|
|
119
|
+
|
|
120
|
+
注意:模板内容已迁移到 templates/ 模块中,此函数现在只是转发调用
|
|
121
|
+
"""
|
|
122
|
+
return get_all_templates(project_name, project_type)
|
tomskit/logger/README.md
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
# Logger Module Guide
|
|
2
|
+
|
|
3
|
+
该模块提供了结构化的日志配置和追踪 ID 支持,支持应用日志、访问日志、SQL 日志和 Celery 日志的分离,适用于 FastAPI 异步环境。
|
|
4
|
+
|
|
5
|
+
## 模块概述
|
|
6
|
+
|
|
7
|
+
Logger 模块基于 Python 标准库 `logging`,提供了完整的日志管理功能。主要特性包括:
|
|
8
|
+
|
|
9
|
+
- 🔍 **追踪 ID 支持**:基于 `ContextVar` 实现线程/协程安全的追踪 ID,支持分布式追踪
|
|
10
|
+
- 📝 **多类型日志**:支持应用日志、访问日志、SQL 日志和 Celery 日志的独立配置
|
|
11
|
+
- 🔄 **日志轮转**:支持按时间自动轮转日志文件,可配置备份数量
|
|
12
|
+
- 🎨 **自定义格式**:支持自定义日志格式,自动注入追踪 ID 和前缀
|
|
13
|
+
- 🔇 **日志降噪**:自动管理第三方库日志级别,减少噪音
|
|
14
|
+
- 🛠️ **配置管理**:基于 Pydantic Settings 的配置类,支持环境变量
|
|
15
|
+
|
|
16
|
+
**Import Path:**
|
|
17
|
+
```python
|
|
18
|
+
from tomskit.logger import (
|
|
19
|
+
setup_logging,
|
|
20
|
+
LoggerConfig,
|
|
21
|
+
set_app_trace_id,
|
|
22
|
+
get_app_trace_id
|
|
23
|
+
)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## 核心类和函数
|
|
27
|
+
|
|
28
|
+
### LoggerConfig
|
|
29
|
+
|
|
30
|
+
日志配置类,继承自 `pydantic_settings.BaseSettings`,用于管理日志系统的各项参数。
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
class LoggerConfig(BaseSettings):
|
|
34
|
+
# === 基础配置 ===
|
|
35
|
+
LOG_PREFIX: str = Field(default="", ...)
|
|
36
|
+
LOG_DIR: str = Field(default="logs", ...)
|
|
37
|
+
LOG_NAME: str = Field(default="apps", ...)
|
|
38
|
+
LOG_LEVEL: str = Field(default="INFO", ...)
|
|
39
|
+
LOG_FORMAT: str = Field(default="[%(asctime)s] [%(levelname)s] [%(name)s] [trace_id=%(trace_id)s] %(message)s", ...)
|
|
40
|
+
LOG_USE_UTC: bool = Field(default=False, ...)
|
|
41
|
+
LOG_DATE_FORMAT: str = Field(default="%Y-%m-%d %H:%M:%S", ...)
|
|
42
|
+
LOG_BACKUP_COUNT: int = Field(default=0, ...)
|
|
43
|
+
LOG_ROTATE_WHEN: str = Field(default="midnight", ...)
|
|
44
|
+
|
|
45
|
+
# === 访问日志配置 ===
|
|
46
|
+
LOG_ACCESS_NAME: str = Field(default="access", ...)
|
|
47
|
+
LOG_ACCESS_FORMAT: str = Field(default='%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"', ...)
|
|
48
|
+
|
|
49
|
+
# === SQL 日志配置 ===
|
|
50
|
+
LOG_SQL_ENABLED: bool = Field(default=False, ...)
|
|
51
|
+
LOG_SQL_NAME: str = Field(default="sql", ...)
|
|
52
|
+
LOG_SQL_LEVEL: str = Field(default="INFO", ...)
|
|
53
|
+
|
|
54
|
+
# === Celery 日志配置 ===
|
|
55
|
+
LOG_CELERY_ENABLED: bool = Field(default=False, ...)
|
|
56
|
+
LOG_CELERY_NAME: str = Field(default="celery", ...)
|
|
57
|
+
LOG_CELERY_LEVEL: str = Field(default="INFO", ...)
|
|
58
|
+
|
|
59
|
+
# === 第三方库日志配置 ===
|
|
60
|
+
LOG_THIRD_PARTY_LEVEL: str = Field(default="WARNING", ...)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**配置属性说明:**
|
|
64
|
+
|
|
65
|
+
**基础配置:**
|
|
66
|
+
- `LOG_PREFIX`: 日志前缀,会添加到每条日志记录中,默认为空字符串
|
|
67
|
+
- `LOG_DIR`: 日志文件存储目录,默认为 `logs`
|
|
68
|
+
- `LOG_NAME`: 应用日志文件名(不含扩展名),默认为 `apps`
|
|
69
|
+
- `LOG_LEVEL`: 应用日志级别,可选值:`DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL`,默认为 `INFO`
|
|
70
|
+
- `LOG_FORMAT`: 应用日志格式,支持 `%(trace_id)s` 和 `%(prefix)s` 占位符,默认为 `[%(asctime)s] [%(levelname)s] [%(name)s] [trace_id=%(trace_id)s] %(message)s`
|
|
71
|
+
- `LOG_USE_UTC`: 是否使用 UTC 时间,默认为 `False`(使用本地时间)
|
|
72
|
+
- `LOG_DATE_FORMAT`: 日志日期时间格式,默认为 `%Y-%m-%d %H:%M:%S`
|
|
73
|
+
- `LOG_BACKUP_COUNT`: 日志文件备份数量,`0` 表示不保留历史文件,默认为 `0`
|
|
74
|
+
- `LOG_ROTATE_WHEN`: 日志轮转时间单位,可选值:`midnight`, `D`, `H`, `M`, `S`,默认为 `midnight`(每天午夜轮转)
|
|
75
|
+
|
|
76
|
+
**访问日志配置:**
|
|
77
|
+
- `LOG_ACCESS_NAME`: 访问日志文件名(不含扩展名),默认为 `access`
|
|
78
|
+
- `LOG_ACCESS_FORMAT`: 访问日志格式(Apache Common Log Format),默认为 `%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"`
|
|
79
|
+
|
|
80
|
+
**SQL 日志配置:**
|
|
81
|
+
- `LOG_SQL_ENABLED`: 是否启用独立的 SQL 日志文件,默认为 `False`(合并到应用日志)
|
|
82
|
+
- `LOG_SQL_NAME`: SQL 日志文件名(不含扩展名),默认为 `sql`
|
|
83
|
+
- `LOG_SQL_LEVEL`: SQL 日志级别,默认为 `INFO`
|
|
84
|
+
|
|
85
|
+
**Celery 日志配置:**
|
|
86
|
+
- `LOG_CELERY_ENABLED`: 是否启用独立的 Celery 日志文件,默认为 `False`(合并到应用日志)
|
|
87
|
+
- `LOG_CELERY_NAME`: Celery 日志文件名(不含扩展名),默认为 `celery`
|
|
88
|
+
- `LOG_CELERY_LEVEL`: Celery 日志级别,默认为 `INFO`
|
|
89
|
+
|
|
90
|
+
**第三方库日志配置:**
|
|
91
|
+
- `LOG_THIRD_PARTY_LEVEL`: 第三方库日志级别(用于降噪),默认为 `WARNING`
|
|
92
|
+
|
|
93
|
+
**使用示例:**
|
|
94
|
+
```python
|
|
95
|
+
from tomskit.logger.config import LoggerConfig
|
|
96
|
+
|
|
97
|
+
# 基础配置
|
|
98
|
+
config = LoggerConfig(
|
|
99
|
+
LOG_DIR="logs",
|
|
100
|
+
LOG_NAME="apps",
|
|
101
|
+
LOG_LEVEL="INFO",
|
|
102
|
+
LOG_PREFIX="[MyApp]"
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# 启用 SQL 日志分离
|
|
106
|
+
sql_config = LoggerConfig(
|
|
107
|
+
LOG_SQL_ENABLED=True,
|
|
108
|
+
LOG_SQL_LEVEL="DEBUG"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# 启用 Celery 日志分离
|
|
112
|
+
celery_config = LoggerConfig(
|
|
113
|
+
LOG_CELERY_ENABLED=True,
|
|
114
|
+
LOG_CELERY_LEVEL="INFO"
|
|
115
|
+
)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### setup_logging
|
|
119
|
+
|
|
120
|
+
配置日志系统的主函数,设置应用日志、访问日志、SQL 日志和 Celery 日志的处理器和格式化器。
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
def setup_logging(settings: LoggerConfig) -> None: ...
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**功能特性:**
|
|
127
|
+
- 自动创建日志目录(如果不存在)
|
|
128
|
+
- 配置控制台和文件处理器
|
|
129
|
+
- 设置日志轮转策略
|
|
130
|
+
- 管理框架日志(uvicorn、fastapi)
|
|
131
|
+
- 管理第三方库日志级别
|
|
132
|
+
- 支持追踪 ID 自动注入
|
|
133
|
+
|
|
134
|
+
**方法说明:**
|
|
135
|
+
- 配置 Root Logger:所有 `getLogger(__name__)` 会冒泡到这里
|
|
136
|
+
- 配置访问日志:单独处理 uvicorn/gunicorn 访问日志,不冒泡到 root
|
|
137
|
+
- 配置框架日志:管理 uvicorn 和 fastapi 日志
|
|
138
|
+
- 配置 SQL 日志:根据 `LOG_SQL_ENABLED` 决定是否分离到独立文件
|
|
139
|
+
- 配置 Celery 日志:根据 `LOG_CELERY_ENABLED` 决定是否分离到独立文件
|
|
140
|
+
- 第三方库降噪:自动设置 HTTP 客户端、AWS SDK 等库的日志级别
|
|
141
|
+
|
|
142
|
+
**异常处理:**
|
|
143
|
+
- `OSError`: 当日志目录创建失败时抛出
|
|
144
|
+
- `PermissionError`: 当没有权限创建日志文件时抛出
|
|
145
|
+
|
|
146
|
+
### TraceIdFormatter
|
|
147
|
+
|
|
148
|
+
追踪 ID 格式化器,继承自 `logging.Formatter`,自动为每条日志记录注入 `trace_id` 和 `prefix`。
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
class TraceIdFormatter(logging.Formatter):
|
|
152
|
+
def __init__(self, prefix: str = "", *args, **kwargs) -> None: ...
|
|
153
|
+
|
|
154
|
+
def format(self, record: logging.LogRecord) -> str: ...
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**功能特性:**
|
|
158
|
+
- 自动从上下文变量获取追踪 ID
|
|
159
|
+
- 自动注入日志前缀
|
|
160
|
+
- 确保所有日志都包含追踪信息
|
|
161
|
+
|
|
162
|
+
**使用说明:**
|
|
163
|
+
- `LOG_FORMAT` 中需要包含 `%(trace_id)s` 和 `%(prefix)s` 才能显示
|
|
164
|
+
- 追踪 ID 通过 `ContextVar` 实现,支持异步环境
|
|
165
|
+
|
|
166
|
+
### Trace ID 管理函数
|
|
167
|
+
|
|
168
|
+
追踪 ID 上下文变量和辅助函数,用于在请求生命周期中管理追踪 ID。
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
def set_app_trace_id(trace_id: str) -> None: ...
|
|
172
|
+
def get_app_trace_id() -> str: ...
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**功能特性:**
|
|
176
|
+
- 使用 `ContextVar` 实现线程/协程安全的上下文变量
|
|
177
|
+
- 支持异步环境(FastAPI、asyncio)
|
|
178
|
+
- 自动添加到所有日志记录中
|
|
179
|
+
|
|
180
|
+
**方法说明:**
|
|
181
|
+
- `set_app_trace_id(trace_id)`: 设置当前请求的追踪 ID,通常在 FastAPI 中间件中调用
|
|
182
|
+
- `get_app_trace_id()`: 获取当前请求的追踪 ID,如果未设置则返回默认值 `"-"`
|
|
183
|
+
|
|
184
|
+
## 完整使用示例
|
|
185
|
+
|
|
186
|
+
### 基础使用
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
from tomskit.logger import setup_logging, LoggerConfig
|
|
190
|
+
|
|
191
|
+
# 创建配置
|
|
192
|
+
config = LoggerConfig(
|
|
193
|
+
LOG_DIR="logs",
|
|
194
|
+
LOG_NAME="apps",
|
|
195
|
+
LOG_LEVEL="INFO",
|
|
196
|
+
LOG_PREFIX="[MyApp]"
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# 初始化日志系统
|
|
200
|
+
setup_logging(config)
|
|
201
|
+
|
|
202
|
+
# 现在可以使用标准 logging 模块
|
|
203
|
+
import logging
|
|
204
|
+
logger = logging.getLogger(__name__)
|
|
205
|
+
|
|
206
|
+
logger.info("这是一条信息日志")
|
|
207
|
+
logger.warning("这是一条警告日志")
|
|
208
|
+
logger.error("这是一条错误日志")
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### 在 FastAPI 中使用
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from fastapi import FastAPI, Request
|
|
215
|
+
from fastapi.middleware.base import BaseHTTPMiddleware
|
|
216
|
+
import uuid
|
|
217
|
+
import logging
|
|
218
|
+
from tomskit.logger import setup_logging, LoggerConfig, set_app_trace_id
|
|
219
|
+
|
|
220
|
+
# 创建配置
|
|
221
|
+
log_config = LoggerConfig(
|
|
222
|
+
LOG_DIR="logs",
|
|
223
|
+
LOG_NAME="apps",
|
|
224
|
+
LOG_LEVEL="INFO",
|
|
225
|
+
LOG_PREFIX="[FastAPI]"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# 初始化日志系统
|
|
229
|
+
setup_logging(log_config)
|
|
230
|
+
|
|
231
|
+
app = FastAPI()
|
|
232
|
+
|
|
233
|
+
# 追踪 ID 中间件
|
|
234
|
+
class TraceIdMiddleware(BaseHTTPMiddleware):
|
|
235
|
+
async def dispatch(self, request: Request, call_next):
|
|
236
|
+
# 生成或获取追踪 ID
|
|
237
|
+
trace_id = request.headers.get("X-Trace-Id", str(uuid.uuid4()))
|
|
238
|
+
set_app_trace_id(trace_id)
|
|
239
|
+
|
|
240
|
+
response = await call_next(request)
|
|
241
|
+
response.headers["X-Trace-Id"] = trace_id
|
|
242
|
+
return response
|
|
243
|
+
|
|
244
|
+
app.add_middleware(TraceIdMiddleware)
|
|
245
|
+
|
|
246
|
+
logger = logging.getLogger(__name__)
|
|
247
|
+
|
|
248
|
+
@app.get("/")
|
|
249
|
+
async def root():
|
|
250
|
+
logger.info("处理根路径请求")
|
|
251
|
+
return {"message": "Hello World"}
|
|
252
|
+
|
|
253
|
+
@app.get("/items/{item_id}")
|
|
254
|
+
async def read_item(item_id: int):
|
|
255
|
+
logger.info(f"读取物品 {item_id}")
|
|
256
|
+
return {"item_id": item_id}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 启用 SQL 日志分离
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
from tomskit.logger import setup_logging, LoggerConfig
|
|
263
|
+
|
|
264
|
+
# 启用 SQL 日志分离
|
|
265
|
+
config = LoggerConfig(
|
|
266
|
+
LOG_SQL_ENABLED=True,
|
|
267
|
+
LOG_SQL_LEVEL="DEBUG", # 查看详细的 SQL 查询
|
|
268
|
+
LOG_SQL_NAME="sql"
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
setup_logging(config)
|
|
272
|
+
|
|
273
|
+
# SQL 日志会写入 logs/sql.log 文件
|
|
274
|
+
# 应用日志会写入 logs/apps.log 文件
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### 启用 Celery 日志分离
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
from tomskit.logger import setup_logging, LoggerConfig
|
|
281
|
+
|
|
282
|
+
# 启用 Celery 日志分离
|
|
283
|
+
config = LoggerConfig(
|
|
284
|
+
LOG_CELERY_ENABLED=True,
|
|
285
|
+
LOG_CELERY_LEVEL="INFO",
|
|
286
|
+
LOG_CELERY_NAME="celery"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
setup_logging(config)
|
|
290
|
+
|
|
291
|
+
# Celery 日志会写入 logs/celery.log 文件
|
|
292
|
+
# 应用日志会写入 logs/apps.log 文件
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 自定义日志格式
|
|
296
|
+
|
|
297
|
+
```python
|
|
298
|
+
from tomskit.logger import setup_logging, LoggerConfig
|
|
299
|
+
|
|
300
|
+
# 自定义日志格式
|
|
301
|
+
config = LoggerConfig(
|
|
302
|
+
LOG_FORMAT="[%(asctime)s] [%(levelname)-8s] [%(name)s] [trace_id=%(trace_id)s] [%(prefix)s] %(message)s",
|
|
303
|
+
LOG_DATE_FORMAT="%Y-%m-%d %H:%M:%S.%f",
|
|
304
|
+
LOG_PREFIX="[MyService]"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
setup_logging(config)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 配置日志轮转
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from tomskit.logger import setup_logging, LoggerConfig
|
|
314
|
+
|
|
315
|
+
# 配置日志轮转:每天午夜轮转,保留 30 个备份文件
|
|
316
|
+
config = LoggerConfig(
|
|
317
|
+
LOG_ROTATE_WHEN="midnight",
|
|
318
|
+
LOG_BACKUP_COUNT=30
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
setup_logging(config)
|
|
322
|
+
|
|
323
|
+
# 或者按小时轮转
|
|
324
|
+
hourly_config = LoggerConfig(
|
|
325
|
+
LOG_ROTATE_WHEN="H",
|
|
326
|
+
LOG_BACKUP_COUNT=24 # 保留 24 小时的历史日志
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
setup_logging(hourly_config)
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### 使用 UTC 时间
|
|
333
|
+
|
|
334
|
+
```python
|
|
335
|
+
from tomskit.logger import setup_logging, LoggerConfig
|
|
336
|
+
|
|
337
|
+
# 使用 UTC 时间
|
|
338
|
+
config = LoggerConfig(
|
|
339
|
+
LOG_USE_UTC=True,
|
|
340
|
+
LOG_DATE_FORMAT="%Y-%m-%d %H:%M:%S UTC"
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
setup_logging(config)
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### 在业务代码中使用追踪 ID
|
|
347
|
+
|
|
348
|
+
```python
|
|
349
|
+
import logging
|
|
350
|
+
from tomskit.logger import get_app_trace_id
|
|
351
|
+
|
|
352
|
+
logger = logging.getLogger(__name__)
|
|
353
|
+
|
|
354
|
+
def process_order(order_id: int):
|
|
355
|
+
trace_id = get_app_trace_id()
|
|
356
|
+
logger.info(f"开始处理订单 {order_id},追踪 ID: {trace_id}")
|
|
357
|
+
|
|
358
|
+
# 业务逻辑...
|
|
359
|
+
|
|
360
|
+
logger.info(f"订单 {order_id} 处理完成")
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### 在异步任务中使用
|
|
364
|
+
|
|
365
|
+
```python
|
|
366
|
+
import asyncio
|
|
367
|
+
import logging
|
|
368
|
+
from tomskit.logger import set_app_trace_id, get_app_trace_id
|
|
369
|
+
|
|
370
|
+
logger = logging.getLogger(__name__)
|
|
371
|
+
|
|
372
|
+
async def async_task(task_id: int, trace_id: str):
|
|
373
|
+
# 在异步任务中设置追踪 ID
|
|
374
|
+
set_app_trace_id(trace_id)
|
|
375
|
+
|
|
376
|
+
logger.info(f"开始执行异步任务 {task_id}")
|
|
377
|
+
|
|
378
|
+
# 业务逻辑...
|
|
379
|
+
await asyncio.sleep(1)
|
|
380
|
+
|
|
381
|
+
logger.info(f"异步任务 {task_id} 执行完成")
|
|
382
|
+
return {"task_id": task_id, "trace_id": get_app_trace_id()}
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## 环境变量配置
|
|
386
|
+
|
|
387
|
+
Logger 模块支持通过环境变量进行配置,所有 `LoggerConfig` 中的字段都可以通过环境变量设置:
|
|
388
|
+
|
|
389
|
+
**基础配置:**
|
|
390
|
+
- `LOG_PREFIX`: 日志前缀
|
|
391
|
+
- `LOG_DIR`: 日志文件存储目录
|
|
392
|
+
- `LOG_NAME`: 应用日志文件名
|
|
393
|
+
- `LOG_LEVEL`: 应用日志级别
|
|
394
|
+
- `LOG_FORMAT`: 应用日志格式
|
|
395
|
+
- `LOG_USE_UTC`: 是否使用 UTC 时间(`true`/`false`)
|
|
396
|
+
- `LOG_DATE_FORMAT`: 日志日期时间格式
|
|
397
|
+
- `LOG_BACKUP_COUNT`: 日志文件备份数量
|
|
398
|
+
- `LOG_ROTATE_WHEN`: 日志轮转时间单位
|
|
399
|
+
|
|
400
|
+
**访问日志配置:**
|
|
401
|
+
- `LOG_ACCESS_NAME`: 访问日志文件名
|
|
402
|
+
- `LOG_ACCESS_FORMAT`: 访问日志格式
|
|
403
|
+
|
|
404
|
+
**SQL 日志配置:**
|
|
405
|
+
- `LOG_SQL_ENABLED`: 是否启用独立的 SQL 日志文件(`true`/`false`)
|
|
406
|
+
- `LOG_SQL_NAME`: SQL 日志文件名
|
|
407
|
+
- `LOG_SQL_LEVEL`: SQL 日志级别
|
|
408
|
+
|
|
409
|
+
**Celery 日志配置:**
|
|
410
|
+
- `LOG_CELERY_ENABLED`: 是否启用独立的 Celery 日志文件(`true`/`false`)
|
|
411
|
+
- `LOG_CELERY_NAME`: Celery 日志文件名
|
|
412
|
+
- `LOG_CELERY_LEVEL`: Celery 日志级别
|
|
413
|
+
|
|
414
|
+
**第三方库日志配置:**
|
|
415
|
+
- `LOG_THIRD_PARTY_LEVEL`: 第三方库日志级别
|
|
416
|
+
|
|
417
|
+
**使用示例:**
|
|
418
|
+
```bash
|
|
419
|
+
# 通过环境变量配置
|
|
420
|
+
export LOG_DIR="/var/log/myapp"
|
|
421
|
+
export LOG_LEVEL="DEBUG"
|
|
422
|
+
export LOG_SQL_ENABLED="true"
|
|
423
|
+
export LOG_CELERY_ENABLED="true"
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
```python
|
|
427
|
+
from tomskit.logger import setup_logging, LoggerConfig
|
|
428
|
+
|
|
429
|
+
# 自动从环境变量读取配置
|
|
430
|
+
config = LoggerConfig()
|
|
431
|
+
setup_logging(config)
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## 日志文件结构
|
|
435
|
+
|
|
436
|
+
配置完成后,日志文件会按照以下结构组织:
|
|
437
|
+
|
|
438
|
+
```
|
|
439
|
+
logs/
|
|
440
|
+
├── apps.log # 应用日志(主日志)
|
|
441
|
+
├── apps.log.2024-01-01 # 轮转后的历史日志
|
|
442
|
+
├── access.log # 访问日志(uvicorn/gunicorn)
|
|
443
|
+
├── access.log.2024-01-01 # 轮转后的访问日志
|
|
444
|
+
├── sql.log # SQL 日志(如果启用)
|
|
445
|
+
├── sql.log.2024-01-01 # 轮转后的 SQL 日志
|
|
446
|
+
├── celery.log # Celery 日志(如果启用)
|
|
447
|
+
└── celery.log.2024-01-01 # 轮转后的 Celery 日志
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
## 注意事项
|
|
451
|
+
|
|
452
|
+
1. **初始化顺序**:在使用日志功能之前必须先调用 `setup_logging()` 进行初始化
|
|
453
|
+
2. **追踪 ID 设置**:追踪 ID 需要在请求开始时设置(通常在中间件中),否则会使用默认值 `"-"`
|
|
454
|
+
3. **日志格式**:如果要在日志中显示追踪 ID 和前缀,`LOG_FORMAT` 中必须包含 `%(trace_id)s` 和 `%(prefix)s`
|
|
455
|
+
4. **日志目录权限**:确保应用有权限在 `LOG_DIR` 指定的目录中创建和写入文件
|
|
456
|
+
5. **日志轮转**:日志轮转会在指定的时间点自动进行,轮转后的文件会添加日期后缀
|
|
457
|
+
6. **备份数量**:`LOG_BACKUP_COUNT=0` 表示不保留历史文件,轮转时会直接覆盖旧文件
|
|
458
|
+
7. **异步环境**:追踪 ID 使用 `ContextVar` 实现,完全支持异步环境,无需担心线程安全问题
|
|
459
|
+
8. **第三方库日志**:模块会自动管理常见第三方库的日志级别,减少日志噪音
|
|
460
|
+
9. **访问日志分离**:访问日志(uvicorn/gunicorn)会自动分离到独立文件,不会冒泡到 root logger
|
|
461
|
+
10. **框架日志管理**:uvicorn 和 fastapi 的日志会自动配置,无需手动设置
|
|
462
|
+
|
|
463
|
+
## 相关文档
|
|
464
|
+
|
|
465
|
+
- [Logger Guide](../../docs/specs/logger_guide.md) - 详细的日志使用指南
|
|
466
|
+
- [Python logging 文档](https://docs.python.org/3/library/logging.html) - Python 标准库日志文档
|
tomskit/logger/config.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
from pydantic_settings import BaseSettings
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class LoggerConfig(BaseSettings):
|
|
6
|
+
"""
|
|
7
|
+
日志配置类
|
|
8
|
+
|
|
9
|
+
用于配置日志系统的各项参数,包括日志级别、格式、文件路径、轮转策略等。
|
|
10
|
+
支持应用日志、访问日志、SQL 日志和 Celery 日志的独立配置。
|
|
11
|
+
"""
|
|
12
|
+
# === 基础配置 ===
|
|
13
|
+
LOG_PREFIX: str = Field(
|
|
14
|
+
description="日志前缀,会添加到每条日志记录中",
|
|
15
|
+
default="",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
LOG_DIR: str = Field(
|
|
19
|
+
description="日志文件存储目录",
|
|
20
|
+
default="logs",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
LOG_NAME: str = Field(
|
|
24
|
+
description="应用日志文件名(不含扩展名)",
|
|
25
|
+
default="apps",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
LOG_LEVEL: str = Field(
|
|
29
|
+
description="应用日志级别 (DEBUG, INFO, WARNING, ERROR, CRITICAL)",
|
|
30
|
+
default="INFO",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
LOG_FORMAT: str = Field(
|
|
34
|
+
description="应用日志格式,支持 %(trace_id)s 和 %(prefix)s 占位符",
|
|
35
|
+
default="[%(asctime)s] [%(levelname)s] [%(name)s] [trace_id=%(trace_id)s] %(message)s",
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
LOG_USE_UTC: bool = Field(
|
|
39
|
+
description="是否使用 UTC 时间",
|
|
40
|
+
default=False,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
LOG_DATE_FORMAT: str = Field(
|
|
44
|
+
description="日志日期时间格式",
|
|
45
|
+
default="%Y-%m-%d %H:%M:%S",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
LOG_BACKUP_COUNT: int = Field(
|
|
49
|
+
description="日志文件备份数量,0 表示不保留历史文件",
|
|
50
|
+
default=0,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
LOG_ROTATE_WHEN: str = Field(
|
|
54
|
+
description="日志轮转时间单位 (midnight, D, H, M, S)",
|
|
55
|
+
default="midnight",
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# === 访问日志配置 ===
|
|
59
|
+
LOG_ACCESS_NAME: str = Field(
|
|
60
|
+
description="访问日志文件名(不含扩展名)",
|
|
61
|
+
default="access",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
LOG_ACCESS_FORMAT: str = Field(
|
|
65
|
+
description="访问日志格式(Apache Common Log Format)",
|
|
66
|
+
default='%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s"',
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# === SQL 日志配置 ===
|
|
70
|
+
LOG_SQL_ENABLED: bool = Field(
|
|
71
|
+
description="是否启用独立的 SQL 日志文件",
|
|
72
|
+
default=False,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
LOG_SQL_NAME: str = Field(
|
|
76
|
+
description="SQL 日志文件名(不含扩展名)",
|
|
77
|
+
default="sql",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
LOG_SQL_LEVEL: str = Field(
|
|
81
|
+
description="SQL 日志级别",
|
|
82
|
+
default="INFO",
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# === Celery 日志配置 ===
|
|
86
|
+
LOG_CELERY_ENABLED: bool = Field(
|
|
87
|
+
description="是否启用独立的 Celery 日志文件",
|
|
88
|
+
default=False,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
LOG_CELERY_NAME: str = Field(
|
|
92
|
+
description="Celery 日志文件名(不含扩展名)",
|
|
93
|
+
default="celery",
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
LOG_CELERY_LEVEL: str = Field(
|
|
97
|
+
description="Celery 日志级别",
|
|
98
|
+
default="INFO",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# === 第三方库日志配置 ===
|
|
102
|
+
LOG_THIRD_PARTY_LEVEL: str = Field(
|
|
103
|
+
description="第三方库日志级别(用于降噪)",
|
|
104
|
+
default="WARNING",
|
|
105
|
+
)
|
|
106
|
+
|