yyds-logger 0.3.0__tar.gz
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.
- yyds_logger-0.3.0/PKG-INFO +760 -0
- yyds_logger-0.3.0/README.md +712 -0
- yyds_logger-0.3.0/setup.cfg +4 -0
- yyds_logger-0.3.0/setup.py +79 -0
- yyds_logger-0.3.0/yyds_logger/__init__.py +40 -0
- yyds_logger-0.3.0/yyds_logger/__version__.py +10 -0
- yyds_logger-0.3.0/yyds_logger/advanced_features.py +930 -0
- yyds_logger-0.3.0/yyds_logger/yyds_logger.py +2473 -0
- yyds_logger-0.3.0/yyds_logger.egg-info/PKG-INFO +760 -0
- yyds_logger-0.3.0/yyds_logger.egg-info/SOURCES.txt +11 -0
- yyds_logger-0.3.0/yyds_logger.egg-info/dependency_links.txt +1 -0
- yyds_logger-0.3.0/yyds_logger.egg-info/requires.txt +19 -0
- yyds_logger-0.3.0/yyds_logger.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yyds_logger
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: An enhanced logger based on Loguru
|
|
5
|
+
Home-page: https://github.com/yyds-fast/yyds-logger
|
|
6
|
+
Author: yyds-fast
|
|
7
|
+
Author-email: yyds.fast@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Bug Reports, https://github.com/yyds-fast/yyds_logger/issues
|
|
10
|
+
Project-URL: Source, https://github.com/yyds-fast/yyds_logger
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Requires-Python: >=3.8
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
Requires-Dist: loguru>=0.7.3
|
|
22
|
+
Provides-Extra: remote
|
|
23
|
+
Requires-Dist: aiohttp>=3.13.2; extra == "remote"
|
|
24
|
+
Requires-Dist: requests>=2.34.2; extra == "remote"
|
|
25
|
+
Provides-Extra: profile
|
|
26
|
+
Requires-Dist: line_profiler>=5.0.2; extra == "profile"
|
|
27
|
+
Provides-Extra: advanced
|
|
28
|
+
Requires-Dist: psutil>=7.2.2; extra == "advanced"
|
|
29
|
+
Requires-Dist: cryptography>=48.0.0; extra == "advanced"
|
|
30
|
+
Provides-Extra: all
|
|
31
|
+
Requires-Dist: aiohttp>=3.13.2; extra == "all"
|
|
32
|
+
Requires-Dist: requests>=2.34.2; extra == "all"
|
|
33
|
+
Requires-Dist: line_profiler>=5.0.2; extra == "all"
|
|
34
|
+
Requires-Dist: psutil>=7.2.2; extra == "all"
|
|
35
|
+
Requires-Dist: cryptography>=48.0.0; extra == "all"
|
|
36
|
+
Dynamic: author
|
|
37
|
+
Dynamic: author-email
|
|
38
|
+
Dynamic: classifier
|
|
39
|
+
Dynamic: description
|
|
40
|
+
Dynamic: description-content-type
|
|
41
|
+
Dynamic: home-page
|
|
42
|
+
Dynamic: license
|
|
43
|
+
Dynamic: project-url
|
|
44
|
+
Dynamic: provides-extra
|
|
45
|
+
Dynamic: requires-dist
|
|
46
|
+
Dynamic: requires-python
|
|
47
|
+
Dynamic: summary
|
|
48
|
+
|
|
49
|
+
# YydsLogger
|
|
50
|
+
|
|
51
|
+
基于 Loguru 的增强日志记录器,支持多语言、异步操作、远程错误上报、统计与日志管理等能力。
|
|
52
|
+
|
|
53
|
+
## 特性
|
|
54
|
+
|
|
55
|
+
- 多语言输出(zh/en)
|
|
56
|
+
- 自定义格式、级别过滤、按大小或按时间轮转、保留策略、压缩
|
|
57
|
+
- **JSON 结构化输出(serialize)**:直接对接 ELK / Loki / Datadog / CloudWatch
|
|
58
|
+
- **接管标准库 logging**:把 uvicorn / sqlalchemy / requests 等三方库日志统一汇入本管道
|
|
59
|
+
- **环境感知(env=dev/prod)**:生产自动关闭 diagnose/backtrace,保持非阻塞写入
|
|
60
|
+
- **按 sink 独立级别**:控制台、主文件、错误文件可各自设级别
|
|
61
|
+
- request_id 上下文注入(ContextVar)+ `bind/contextualize` 结构化字段与 trace 关联
|
|
62
|
+
- 装饰器记录函数调用与耗时(同步/异步),级别关闭时自动跳过昂贵格式化
|
|
63
|
+
- 远程日志上报(默认仅 ERROR 及以上,异步发送避免阻塞,带队列背压保护)
|
|
64
|
+
- 基础统计(按级别/分类/小时)、缓存性能信息
|
|
65
|
+
- 日志管理(压缩、归档、清理,自动跳过活跃文件)、简单分析与导出
|
|
66
|
+
- **生产友好**:核心仅依赖 loguru;SIGTERM 优雅排空;自适应级别零 handler 重建
|
|
67
|
+
|
|
68
|
+
## 安装
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pip install -U yyds_logger
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
核心安装只依赖 `loguru`。按需安装可选能力:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install "yyds_logger[remote]" # 远程日志上报(aiohttp + requests)
|
|
78
|
+
pip install "yyds_logger[profile]" # 行级耗时分析(line_profiler,更精确)
|
|
79
|
+
pip install "yyds_logger[advanced]" # 高级模块(psutil 系统指标 + cryptography 加密)
|
|
80
|
+
pip install "yyds_logger[all]" # 一次性安装全部可选依赖
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
> 缺失可选依赖时会自动优雅降级:例如未装 aiohttp/requests 时远程上报自动禁用并告警一次,
|
|
84
|
+
> 未装 line_profiler 时 `time_it(line_by_line=True)` 自动回退到原生 `sys.settrace`。
|
|
85
|
+
|
|
86
|
+
## 快速开始
|
|
87
|
+
|
|
88
|
+
### 基本使用
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
from yyds_logger import YydsLogger
|
|
92
|
+
|
|
93
|
+
# 创建日志记录器实例
|
|
94
|
+
logger = YydsLogger(
|
|
95
|
+
file_name="app",
|
|
96
|
+
log_dir="logs",
|
|
97
|
+
language="zh" # 使用中文输出
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# 设置 request_id(每条日志会带 ReqID:xxx)
|
|
101
|
+
token = logger.request_id_var.set("req-001")
|
|
102
|
+
try:
|
|
103
|
+
logger.info("这是一条信息日志")
|
|
104
|
+
logger.warning("这是一条警告日志")
|
|
105
|
+
logger.error("这是一条错误日志")
|
|
106
|
+
finally:
|
|
107
|
+
logger.request_id_var.reset(token)
|
|
108
|
+
|
|
109
|
+
# 程序退出前清理(关闭远程发送、恢复 excepthook、移除 handler)
|
|
110
|
+
logger.cleanup()
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### request_id 用法与并发说明
|
|
114
|
+
|
|
115
|
+
`request_id_var` 是一个 `ContextVar`,用来给“同一条业务链路”的日志自动带上 `ReqID:...`:
|
|
116
|
+
|
|
117
|
+
- `set(value)`:把当前上下文的 request_id 设为 `value`,并返回一个 token(表示 set 之前的旧值)
|
|
118
|
+
- `reset(token)`:把当前上下文恢复到 set 之前的值,避免 request_id 泄漏到下一次请求/任务
|
|
119
|
+
|
|
120
|
+
推荐写法用 `try/finally` 确保一定 reset(如上例)。
|
|
121
|
+
|
|
122
|
+
多并发下的行为:
|
|
123
|
+
|
|
124
|
+
- 多线程:不同线程之间的 `request_id` 互不影响;但新线程不会自动继承父线程的 request_id,需要显式传递。
|
|
125
|
+
- asyncio:不同 Task 之间的 `request_id` 互不影响;创建 Task 时会复制一份当前上下文,所以要在 `create_task()` 之前 set。
|
|
126
|
+
|
|
127
|
+
线程/线程池中传递 request_id 的示例:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
import contextvars
|
|
131
|
+
import threading
|
|
132
|
+
|
|
133
|
+
token = logger.request_id_var.set("req-001")
|
|
134
|
+
try:
|
|
135
|
+
ctx = contextvars.copy_context()
|
|
136
|
+
t = threading.Thread(target=lambda: ctx.run(logger.info, "子线程日志也带 ReqID"))
|
|
137
|
+
t.start()
|
|
138
|
+
t.join()
|
|
139
|
+
finally:
|
|
140
|
+
logger.request_id_var.reset(token)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
asyncio 并发示例(每个请求独立 request_id):
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
import asyncio
|
|
147
|
+
|
|
148
|
+
async def handle(req_id: str):
|
|
149
|
+
token = logger.request_id_var.set(req_id)
|
|
150
|
+
try:
|
|
151
|
+
logger.info("开始处理")
|
|
152
|
+
await asyncio.sleep(0.1)
|
|
153
|
+
logger.info("处理完成")
|
|
154
|
+
finally:
|
|
155
|
+
logger.request_id_var.reset(token)
|
|
156
|
+
|
|
157
|
+
async def main():
|
|
158
|
+
await asyncio.gather(handle("req-1"), handle("req-2"))
|
|
159
|
+
|
|
160
|
+
asyncio.run(main())
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 生产环境推荐配置(env)
|
|
164
|
+
|
|
165
|
+
`env` 优先于旧的 `work_type`。生产模式会关闭 `diagnose/backtrace`(避免在日志里泄漏变量值并降低开销),
|
|
166
|
+
同时保持 `enqueue=True`(异步非阻塞写入):
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
logger = YydsLogger(
|
|
170
|
+
file_name="app",
|
|
171
|
+
log_dir="logs",
|
|
172
|
+
env="prod", # dev / prod;生产关闭诊断回溯,保持非阻塞
|
|
173
|
+
serialize=True, # 文件输出 JSON,便于日志平台采集
|
|
174
|
+
filter_level="INFO", # 生产通常 INFO 起步
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
如需精细控制,可用 `enqueue` / `diagnose` / `backtrace` 三个可选参数显式覆盖:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
logger = YydsLogger("app", env="prod", diagnose=True) # 临时排障:单独打开 diagnose
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
> 兼容性:不传 `env` 时 `work_type` 行为完全不变(`False`=测试,`True`=旧生产)。
|
|
185
|
+
|
|
186
|
+
### JSON 结构化日志(serialize)
|
|
187
|
+
|
|
188
|
+
容器 / k8s / 日志平台(ELK、Loki、Datadog)更适合消费 JSON。`serialize` 控制文件输出,
|
|
189
|
+
`console_serialize` 控制控制台输出:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
logger = YydsLogger(
|
|
193
|
+
file_name="app",
|
|
194
|
+
serialize=True, # 文件写 JSON(每行一条)
|
|
195
|
+
console_serialize=False, # 控制台仍保留彩色可读格式
|
|
196
|
+
)
|
|
197
|
+
logger.info("用户登录")
|
|
198
|
+
# 文件中为一行 JSON,包含 time/level/message/extra(含 request_id 及 bind/contextualize 字段)
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
> 注意:序列化模式下日志为 JSON,自定义彩色 `format` 的颜色不再生效(这是 loguru 序列化语义)。
|
|
202
|
+
|
|
203
|
+
### 结构化上下文与 trace 关联(bind / contextualize)
|
|
204
|
+
|
|
205
|
+
配合 `serialize=True`,绑定的字段会自动出现在 JSON 中,方便与 OpenTelemetry / 链路追踪对齐:
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
# 永久绑定,返回带上下文的 logger
|
|
209
|
+
logger.bind(service="order", trace_id="abc123").info("处理下单")
|
|
210
|
+
|
|
211
|
+
# with 作用域内临时注入(线程/协程安全)
|
|
212
|
+
with logger.contextualize(trace_id="abc123", span_id="01"):
|
|
213
|
+
logger.info("查询库存")
|
|
214
|
+
logger.info("扣减库存")
|
|
215
|
+
|
|
216
|
+
# request_id 便捷方法
|
|
217
|
+
token = logger.set_request_id("req-001")
|
|
218
|
+
logger.info(logger.get_request_id())
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 接管标准库 logging
|
|
222
|
+
|
|
223
|
+
把三方库(uvicorn、sqlalchemy、requests 等)通过标准库 `logging` 输出的日志统一汇入本管道:
|
|
224
|
+
|
|
225
|
+
```python
|
|
226
|
+
logger = YydsLogger("app", capture_std_logging=True) # 构造时直接接管 root
|
|
227
|
+
|
|
228
|
+
# 或运行时按需接管指定 logger
|
|
229
|
+
logger.capture_std_logging(level="INFO", names=["uvicorn", "sqlalchemy.engine"])
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
`cleanup()` 时会自动恢复被修改的标准库 logging 状态。
|
|
233
|
+
|
|
234
|
+
### 按 sink 设置独立级别
|
|
235
|
+
|
|
236
|
+
控制台、主文件、错误文件可分别设级别(例如控制台只看 WARNING,文件留全量 DEBUG):
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
logger = YydsLogger(
|
|
240
|
+
file_name="app",
|
|
241
|
+
filter_level="DEBUG", # 默认级别
|
|
242
|
+
console_level="WARNING",
|
|
243
|
+
file_level="DEBUG",
|
|
244
|
+
error_level="ERROR",
|
|
245
|
+
)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 优雅退出(SIGTERM)
|
|
249
|
+
|
|
250
|
+
容器 / k8s 用 SIGTERM 停服时 `atexit` 不一定触发,开启后会在退出前排空 enqueue 队列,避免丢日志:
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
logger = YydsLogger("app", install_signal_handlers=True) # 拦截并链回原 SIGTERM/SIGINT 处理器
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### 环境变量配置(read_env)
|
|
257
|
+
|
|
258
|
+
适合多环境部署,无需改代码即可覆盖配置:
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
# 环境变量:YYDS_LOG_DIR / YYDS_LOG_LEVEL / YYDS_LOG_LANG / YYDS_LOG_SERIALIZE / YYDS_LOG_ENV
|
|
262
|
+
logger = YydsLogger("app", read_env=True)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### 异步函数支持
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
import asyncio
|
|
269
|
+
|
|
270
|
+
@logger.log_decorator()
|
|
271
|
+
async def async_function():
|
|
272
|
+
await asyncio.sleep(1)
|
|
273
|
+
return "异步操作完成"
|
|
274
|
+
|
|
275
|
+
# 使用异步函数
|
|
276
|
+
async def main():
|
|
277
|
+
result = await async_function()
|
|
278
|
+
logger.info(f"异步函数结果: {result}")
|
|
279
|
+
|
|
280
|
+
asyncio.run(main())
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 增强错误信息
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
# 错误日志现在会显示详细的位置信息
|
|
287
|
+
@logger.log_decorator("除零错误", level="ERROR")
|
|
288
|
+
def divide_numbers(a, b):
|
|
289
|
+
return a / b
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
result = divide_numbers(1, 0)
|
|
293
|
+
except ZeroDivisionError:
|
|
294
|
+
logger.exception("捕获到除零错误")
|
|
295
|
+
# 输出示例:
|
|
296
|
+
# 2025-01-03 10:30:15.123 | ERROR | ReqID:REQ-123 | app.py:25:divide_numbers | 12345 | 除零错误 [ZeroDivisionError]: division by zero | 位置: app.py:25:divide_numbers | 代码: return a / b
|
|
297
|
+
# 调用链: app.py:25:divide_numbers -> main.py:10:main
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### 耗时统计与行分析 (@time_it)
|
|
301
|
+
|
|
302
|
+
提供 `@logger.time_it` 装饰器,不仅可以记录函数的整体执行耗时,还能开启行级性能分析,扒开函数内部看哪一行代码在拖后腿(支持同步和异步函数)。
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
import time
|
|
306
|
+
import asyncio
|
|
307
|
+
|
|
308
|
+
# 1. 最简使用:只查看耗时,不带括号直接装饰(每次都打印耗时)
|
|
309
|
+
@logger.time_it
|
|
310
|
+
def my_task():
|
|
311
|
+
time.sleep(0.05)
|
|
312
|
+
|
|
313
|
+
my_task()
|
|
314
|
+
# 输出示例:
|
|
315
|
+
# 2026-05-25 15:37:14.790 | INFO | ReqID:no-request-id | yyds_logger.py:778 | [TIMER] 函数 `my_task` 执行完毕 | 总耗时: 52.15ms
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
# 2. 进阶版:开启行级性能分析(支持可选依赖 line_profiler,未安装时自动降级为原生 sys.settrace)
|
|
319
|
+
@logger.time_it(line_by_line=True)
|
|
320
|
+
def heavy_task():
|
|
321
|
+
a = 1 + 2
|
|
322
|
+
time.sleep(0.02) # 耗时较短
|
|
323
|
+
b = a * 3
|
|
324
|
+
time.sleep(0.08) # 耗时最长,被标记为瓶颈
|
|
325
|
+
return b
|
|
326
|
+
|
|
327
|
+
heavy_task()
|
|
328
|
+
# 输出示例:
|
|
329
|
+
# ================================================================================
|
|
330
|
+
# [FN-TIMER] 行性能分析报告 -> 函数: `heavy_task`
|
|
331
|
+
# ================================================================================
|
|
332
|
+
# 行号 | 命中次数 | 总耗时 (ms) | 每步耗时 (ms) | 耗时占比 | 源代码
|
|
333
|
+
# --------------------------------------------------------------------------------
|
|
334
|
+
# 45 | 1 | 0.00 | 0.00 | 0.0% | a = 1 + 2
|
|
335
|
+
# 46 | 1 | 25.05 | 25.05 | 22.8% | time.sleep(0.02) # 这行稍慢 <-- 🚨 性能瓶颈
|
|
336
|
+
# 47 | 1 | 0.00 | 0.00 | 0.0% | b = a * 3
|
|
337
|
+
# 48 | 1 | 84.66 | 84.66 | 77.1% | time.sleep(0.08) # 这行最慢,应当被标记为瓶颈 <-- 🚨 性能瓶颈
|
|
338
|
+
# 49 | 1 | 0.00 | 0.00 | 0.0% | return b
|
|
339
|
+
# --------------------------------------------------------------------------------
|
|
340
|
+
# 总耗时: 109.78 毫秒
|
|
341
|
+
# ================================================================================
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### 带位置信息的日志
|
|
345
|
+
|
|
346
|
+
```python
|
|
347
|
+
# 使用 log_with_location 方法记录带位置信息的日志
|
|
348
|
+
logger.log_with_location("INFO", "这是带位置信息的日志")
|
|
349
|
+
# 输出示例:
|
|
350
|
+
# 2025-01-03 10:30:15.123 | INFO | ReqID:REQ-123 | app.py:30:main | 12345 | [app.py:30:main] 这是带位置信息的日志
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 性能监控
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
import json
|
|
357
|
+
|
|
358
|
+
# 获取性能统计信息
|
|
359
|
+
perf_stats = logger.get_performance_stats()
|
|
360
|
+
print(json.dumps(perf_stats, indent=2))
|
|
361
|
+
|
|
362
|
+
# 清除缓存
|
|
363
|
+
logger.clear_caches()
|
|
364
|
+
|
|
365
|
+
# 性能优化配置
|
|
366
|
+
logger = YydsLogger(
|
|
367
|
+
file_name="app",
|
|
368
|
+
cache_size=512, # 增加缓存大小
|
|
369
|
+
enable_stats=True # 启用统计功能
|
|
370
|
+
)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### 批量日志处理
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
import asyncio
|
|
377
|
+
|
|
378
|
+
# 批量记录日志
|
|
379
|
+
batch_logs = [
|
|
380
|
+
{'level': 'INFO', 'message': '消息1', 'tag': 'BATCH'},
|
|
381
|
+
{'level': 'WARNING', 'message': '消息2', 'category': 'SYSTEM'},
|
|
382
|
+
{'level': 'ERROR', 'message': '消息3'}
|
|
383
|
+
]
|
|
384
|
+
|
|
385
|
+
logger.batch_log(batch_logs) # 同步批量记录
|
|
386
|
+
asyncio.run(logger.async_batch_log(batch_logs)) # 异步批量记录
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### 上下文日志
|
|
390
|
+
|
|
391
|
+
```python
|
|
392
|
+
# 带上下文的日志记录
|
|
393
|
+
context = {
|
|
394
|
+
'user_id': 12345,
|
|
395
|
+
'session_id': 'sess_abc123',
|
|
396
|
+
'request_id': 'req_xyz789'
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
logger.log_with_context("INFO", "用户登录", context)
|
|
400
|
+
logger.log_with_timing("INFO", "API请求完成", {'db_query': 0.125, 'total': 0.467})
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### 自适应日志级别
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
# 启用自适应级别
|
|
407
|
+
logger = YydsLogger(
|
|
408
|
+
file_name="app",
|
|
409
|
+
adaptive_level=True, # 启用自适应级别
|
|
410
|
+
enable_stats=True # 自适应依赖统计
|
|
411
|
+
)
|
|
412
|
+
|
|
413
|
+
# 根据错误率/日志速率自动调整级别(需持续产生日志以更新统计)
|
|
414
|
+
logger.set_adaptive_level(error_rate_threshold=0.1)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### 日志管理
|
|
418
|
+
|
|
419
|
+
> 通常无需手动调用:Loguru 已内置 `rotation/compression/retention`。以下方法会**自动跳过当前活跃日志文件**,
|
|
420
|
+
> 仅处理已轮转的历史文件,避免与正在写入的文件竞争导致日志损坏。
|
|
421
|
+
|
|
422
|
+
```python
|
|
423
|
+
# 压缩旧日志(已轮转文件)
|
|
424
|
+
logger.compress_logs(days_old=7)
|
|
425
|
+
|
|
426
|
+
# 归档日志(已轮转文件)
|
|
427
|
+
logger.archive_logs()
|
|
428
|
+
|
|
429
|
+
# 清理旧日志
|
|
430
|
+
logger.cleanup_old_logs(max_days=30)
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 日志分析
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
# 分析日志
|
|
437
|
+
analysis = logger.analyze_logs(hours=24)
|
|
438
|
+
print(f"错误率: {analysis['error_rate']:.2%}")
|
|
439
|
+
|
|
440
|
+
# 生成报告
|
|
441
|
+
report = logger.generate_log_report(hours=24)
|
|
442
|
+
print(report)
|
|
443
|
+
|
|
444
|
+
# 导出日志
|
|
445
|
+
logger.export_logs_to_json("logs.json", hours=24)
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
### advanced_features(可选)
|
|
449
|
+
|
|
450
|
+
加密功能依赖 cryptography,性能监控的系统指标依赖 psutil:
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
pip install yyds_logger[advanced]
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
```python
|
|
457
|
+
import hashlib
|
|
458
|
+
import time
|
|
459
|
+
from datetime import datetime
|
|
460
|
+
|
|
461
|
+
from yyds_logger.advanced_features import (
|
|
462
|
+
DistributedLogger,
|
|
463
|
+
LogAggregator,
|
|
464
|
+
LogAnalyzer,
|
|
465
|
+
LogArchiver,
|
|
466
|
+
LogBackupManager,
|
|
467
|
+
LogDatabase,
|
|
468
|
+
LogHealthChecker,
|
|
469
|
+
LogSecurity,
|
|
470
|
+
LogStreamProcessor,
|
|
471
|
+
PerformanceMonitor,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
# 智能日志分析(规则匹配)
|
|
475
|
+
analyzer = LogAnalyzer()
|
|
476
|
+
analysis = analyzer.analyze_log({
|
|
477
|
+
'message': '数据库连接失败: Connection refused',
|
|
478
|
+
'level': 'ERROR'
|
|
479
|
+
})
|
|
480
|
+
print(f"严重程度: {analysis['severity']}")
|
|
481
|
+
print(f"类别: {analysis['categories']}")
|
|
482
|
+
print(f"建议: {analysis['suggestions']}")
|
|
483
|
+
|
|
484
|
+
# 分布式日志 ID(跨进程重启也能递增)
|
|
485
|
+
dist_logger = DistributedLogger("node-001", persist_every=100)
|
|
486
|
+
log_id = dist_logger.get_log_id()
|
|
487
|
+
logger.info(f"分布式日志消息 (ID: {log_id})")
|
|
488
|
+
|
|
489
|
+
# 日志安全:脱敏(支持 password/token/api_key/密码/口令 等)
|
|
490
|
+
security = LogSecurity()
|
|
491
|
+
original = "用户密码: 123456, token=abcd"
|
|
492
|
+
sanitized = security.sanitize_message(original)
|
|
493
|
+
print(sanitized)
|
|
494
|
+
|
|
495
|
+
payload = {"user": "alice", "password": "123456", "nested": {"api_key": "k-xxx"}}
|
|
496
|
+
print(security.sanitize_mapping(payload))
|
|
497
|
+
|
|
498
|
+
# 性能监控
|
|
499
|
+
monitor = PerformanceMonitor()
|
|
500
|
+
monitor.record_log("INFO", 0.05)
|
|
501
|
+
metrics = monitor.get_metrics()
|
|
502
|
+
print(f"总日志数: {metrics['log_count']}")
|
|
503
|
+
print(f"平均处理时间: {metrics['avg_processing_time_ms']:.2f}ms")
|
|
504
|
+
monitor.stop()
|
|
505
|
+
|
|
506
|
+
# 日志聚合(去重合并重复日志)
|
|
507
|
+
aggregator = LogAggregator(window_size=100, flush_interval=5.0)
|
|
508
|
+
for i in range(20):
|
|
509
|
+
aggregator.add_log({
|
|
510
|
+
'level': 'INFO',
|
|
511
|
+
'message': '重复的日志消息',
|
|
512
|
+
'timestamp': time.time()
|
|
513
|
+
})
|
|
514
|
+
aggregated = aggregator.flush()
|
|
515
|
+
print(aggregated[0]["message"])
|
|
516
|
+
aggregator.stop()
|
|
517
|
+
|
|
518
|
+
# 流处理(管道式加工日志 entry)
|
|
519
|
+
processor = LogStreamProcessor(max_queue_size=1000)
|
|
520
|
+
|
|
521
|
+
def add_timestamp(log_entry):
|
|
522
|
+
log_entry['processed_timestamp'] = time.time()
|
|
523
|
+
return log_entry
|
|
524
|
+
|
|
525
|
+
def add_checksum(log_entry):
|
|
526
|
+
message = log_entry.get('message', '')
|
|
527
|
+
log_entry['checksum'] = hashlib.md5(message.encode()).hexdigest()[:8]
|
|
528
|
+
return log_entry
|
|
529
|
+
|
|
530
|
+
processor.add_processor(add_timestamp)
|
|
531
|
+
processor.add_processor(add_checksum)
|
|
532
|
+
|
|
533
|
+
# 处理日志
|
|
534
|
+
processor.process_log({'level': 'INFO', 'message': '测试消息'})
|
|
535
|
+
processed_log = processor.get_processed_log(timeout=1.0)
|
|
536
|
+
processor.stop()
|
|
537
|
+
|
|
538
|
+
# SQLite 数据库存储(结构化落库 + 条件查询)
|
|
539
|
+
db = LogDatabase("logs.db")
|
|
540
|
+
db.insert_log({
|
|
541
|
+
'timestamp': datetime.now().isoformat(),
|
|
542
|
+
'level': 'ERROR',
|
|
543
|
+
'message': '数据库连接失败',
|
|
544
|
+
'file': 'app.py',
|
|
545
|
+
'line': 100,
|
|
546
|
+
'function': 'connect_db'
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
# 查询错误日志
|
|
550
|
+
logs = db.query_logs({'level': 'ERROR'}, limit=10)
|
|
551
|
+
db.close()
|
|
552
|
+
|
|
553
|
+
# 健康检查
|
|
554
|
+
checker = LogHealthChecker()
|
|
555
|
+
health = checker.check_health("logs")
|
|
556
|
+
print(f"状态: {health['status']}")
|
|
557
|
+
print(f"磁盘使用率: {health['disk_usage_percent']:.1f}%")
|
|
558
|
+
|
|
559
|
+
# 安全备份/恢复(防止 tar 路径穿越)
|
|
560
|
+
backup_mgr = LogBackupManager("backups")
|
|
561
|
+
backup_path = backup_mgr.create_backup("logs", "daily_backup")
|
|
562
|
+
|
|
563
|
+
# 列出备份
|
|
564
|
+
backups = backup_mgr.list_backups()
|
|
565
|
+
for backup in backups:
|
|
566
|
+
print(f"{backup['name']} - {backup['size_mb']:.2f}MB")
|
|
567
|
+
|
|
568
|
+
# 日志归档(压缩并移到 archives 目录)
|
|
569
|
+
archiver = LogArchiver("archives")
|
|
570
|
+
archived_files = archiver.archive_logs("logs", days_old=7, compression_type="gzip")
|
|
571
|
+
print(f"归档了 {len(archived_files)} 个文件")
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### 远程日志收集
|
|
575
|
+
|
|
576
|
+
需要安装 remote 可选依赖:`pip install "yyds_logger[remote]"`。默认仅上报 ERROR 及以上级别,
|
|
577
|
+
异步发送(优先 aiohttp,回退 requests)。队列带背压保护,远端不可用时丢弃并计数,避免内存无限增长。
|
|
578
|
+
|
|
579
|
+
```python
|
|
580
|
+
logger = YydsLogger(
|
|
581
|
+
file_name="app",
|
|
582
|
+
remote_log_url="https://your-log-server.com/logs",
|
|
583
|
+
max_workers=3,
|
|
584
|
+
remote_queue_size=10000, # 队列上限,满则丢弃并计数(背压保护)
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
# 查看因背压被丢弃的远程日志条数
|
|
588
|
+
print(logger.get_remote_dropped())
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
### 日志统计功能
|
|
592
|
+
|
|
593
|
+
```python
|
|
594
|
+
# 启用统计功能
|
|
595
|
+
logger = YydsLogger(
|
|
596
|
+
file_name="app",
|
|
597
|
+
enable_stats=True
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
# 获取统计信息
|
|
601
|
+
stats = logger.get_stats()
|
|
602
|
+
print(logger.get_stats_summary())
|
|
603
|
+
|
|
604
|
+
# 获取错误趋势
|
|
605
|
+
error_trend = logger.get_error_trend()
|
|
606
|
+
|
|
607
|
+
# 获取分类分布
|
|
608
|
+
category_dist = logger.get_category_distribution()
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
## 高级配置
|
|
612
|
+
|
|
613
|
+
### 完整配置示例
|
|
614
|
+
|
|
615
|
+
```python
|
|
616
|
+
logger = YydsLogger(
|
|
617
|
+
file_name="app", # 日志文件名
|
|
618
|
+
log_dir="logs", # 日志目录
|
|
619
|
+
max_size=14, # 单个日志文件最大大小(MB)
|
|
620
|
+
retention="7 days", # 日志保留时间
|
|
621
|
+
remote_log_url=None, # 远程日志服务器URL(需 [remote] 依赖)
|
|
622
|
+
max_workers=3, # 远程日志发送线程数
|
|
623
|
+
work_type=False, # 已废弃,建议用 env;False=测试,True=旧生产
|
|
624
|
+
language="zh", # 日志语言(zh/en)
|
|
625
|
+
rotation_time="1 day", # 日志轮转时间
|
|
626
|
+
custom_format=None, # 自定义日志格式
|
|
627
|
+
filter_level="DEBUG", # 默认日志过滤级别
|
|
628
|
+
compression="zip", # 日志压缩格式
|
|
629
|
+
enable_stats=False, # 是否启用统计
|
|
630
|
+
categories=None, # 日志分类列表
|
|
631
|
+
cache_size=128, # 缓存大小
|
|
632
|
+
adaptive_level=False, # 自适应日志级别(开启后改级别零 handler 重建)
|
|
633
|
+
performance_mode=False, # 性能模式(配合 enable_performance_mode/disable_performance_mode)
|
|
634
|
+
enable_exception_hook=False, # 是否接管 sys.excepthook(同时兜底子线程异常)
|
|
635
|
+
# —— 新增参数 ——
|
|
636
|
+
env=None, # "dev"/"prod",优先于 work_type
|
|
637
|
+
enqueue=None, # 显式覆盖 enqueue(异步非阻塞写入)
|
|
638
|
+
diagnose=None, # 显式覆盖 diagnose
|
|
639
|
+
backtrace=None, # 显式覆盖 backtrace
|
|
640
|
+
serialize=False, # 文件输出 JSON 结构化日志
|
|
641
|
+
console_serialize=False, # 控制台输出 JSON 结构化日志
|
|
642
|
+
console_level=None, # 控制台独立级别(默认随 filter_level)
|
|
643
|
+
file_level=None, # 主文件独立级别(默认随 filter_level)
|
|
644
|
+
error_level="ERROR", # 错误文件级别
|
|
645
|
+
capture_std_logging=False, # 接管标准库 logging
|
|
646
|
+
install_signal_handlers=False, # 注册 SIGTERM/SIGINT 优雅退出
|
|
647
|
+
remote_queue_size=10000, # 远程日志队列上限(背压保护)
|
|
648
|
+
read_env=False, # 从环境变量读取配置覆盖
|
|
649
|
+
)
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
### 自定义日志格式
|
|
653
|
+
|
|
654
|
+
```python
|
|
655
|
+
custom_format = (
|
|
656
|
+
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
|
|
657
|
+
"<level>{level: <8}</level> | "
|
|
658
|
+
"ReqID:{extra[request_id]} | "
|
|
659
|
+
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
|
|
660
|
+
"<level>{message}</level>"
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
logger = YydsLogger(
|
|
664
|
+
file_name="app",
|
|
665
|
+
custom_format=custom_format
|
|
666
|
+
)
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
## 主要功能
|
|
670
|
+
|
|
671
|
+
### 1. 日志记录
|
|
672
|
+
- 支持所有标准日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)
|
|
673
|
+
- 支持自定义日志级别
|
|
674
|
+
- 支持带标签和分类的日志记录
|
|
675
|
+
|
|
676
|
+
### 2. 日志管理
|
|
677
|
+
- 自动日志轮转
|
|
678
|
+
- 日志压缩
|
|
679
|
+
- 日志保留策略
|
|
680
|
+
- 多文件输出(按级别分文件)
|
|
681
|
+
|
|
682
|
+
### 3. 统计功能
|
|
683
|
+
- 日志总数统计
|
|
684
|
+
- 错误率统计
|
|
685
|
+
- 按类别统计
|
|
686
|
+
- 按时间统计
|
|
687
|
+
- 错误趋势分析
|
|
688
|
+
|
|
689
|
+
### 4. 远程日志
|
|
690
|
+
- 异步远程日志发送
|
|
691
|
+
- 自动重试机制
|
|
692
|
+
- 线程池管理
|
|
693
|
+
- 错误处理
|
|
694
|
+
|
|
695
|
+
### 5. 装饰器支持
|
|
696
|
+
- 函数执行时间记录
|
|
697
|
+
- 异常捕获和记录
|
|
698
|
+
- 支持同步和异步函数
|
|
699
|
+
|
|
700
|
+
### 6. 增强错误信息
|
|
701
|
+
- 显示错误发生的具体文件、行号和函数名
|
|
702
|
+
- 显示错误发生时的代码行内容
|
|
703
|
+
- 显示调用链信息(最后3层调用)
|
|
704
|
+
- 支持全局异常处理器
|
|
705
|
+
- 提供带位置信息的日志记录方法
|
|
706
|
+
|
|
707
|
+
### 7. 性能优化
|
|
708
|
+
- 智能缓存机制减少重复计算
|
|
709
|
+
- 连接池优化网络请求性能
|
|
710
|
+
- 内存优化减少对象创建
|
|
711
|
+
- 统计缓存提高查询效率
|
|
712
|
+
- 线程本地缓存提升并发性能
|
|
713
|
+
|
|
714
|
+
### 8. 高级功能
|
|
715
|
+
- 批量日志处理提高大量日志记录性能
|
|
716
|
+
- 上下文日志自动添加相关上下文信息
|
|
717
|
+
- 自适应级别根据系统状态自动调整日志级别
|
|
718
|
+
- 日志管理压缩、归档、清理功能
|
|
719
|
+
- 日志分析智能分析日志内容和趋势
|
|
720
|
+
- 性能监控实时监控缓存和性能指标
|
|
721
|
+
|
|
722
|
+
### 9. 智能分析功能
|
|
723
|
+
- 智能日志分析自动识别错误、警告、安全事件
|
|
724
|
+
- 分布式日志支持多节点环境,提供唯一日志ID
|
|
725
|
+
- 日志安全功能敏感信息清理和加密
|
|
726
|
+
- 性能监控实时监控系统资源使用
|
|
727
|
+
- 日志聚合自动聚合重复日志
|
|
728
|
+
- 流处理可扩展的日志处理管道
|
|
729
|
+
- 数据库支持结构化日志存储和查询
|
|
730
|
+
- 健康检查系统状态监控
|
|
731
|
+
- 备份管理自动备份和恢复
|
|
732
|
+
- 内存优化智能垃圾回收
|
|
733
|
+
- 智能路由基于条件的日志分发
|
|
734
|
+
- 日志归档自动压缩和归档
|
|
735
|
+
|
|
736
|
+
## 错误处理
|
|
737
|
+
|
|
738
|
+
```python
|
|
739
|
+
try:
|
|
740
|
+
logger = YydsLogger("app", log_dir="/path/to/logs")
|
|
741
|
+
except RuntimeError as e:
|
|
742
|
+
print(f"日志配置失败: {e}")
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
## 注意事项
|
|
746
|
+
|
|
747
|
+
1. 确保日志目录具有写入权限
|
|
748
|
+
2. 远程日志URL必须是有效的HTTP(S)地址,且需安装 `yyds_logger[remote]`
|
|
749
|
+
3. 生产环境建议使用 `env="prod"` + `serialize=True`,并按需 `install_signal_handlers=True`
|
|
750
|
+
4. 异步操作时注意正确处理异常
|
|
751
|
+
5. `serialize=True` 时日志为 JSON,自定义彩色 `format` 的颜色不再生效
|
|
752
|
+
6. 全局 logger 为 loguru 单例,建议单进程内仅实例化一个 YydsLogger,避免多实例 handler 互相叠加
|
|
753
|
+
|
|
754
|
+
## 贡献
|
|
755
|
+
|
|
756
|
+
欢迎提交 Issue 和 Pull Request!
|
|
757
|
+
|
|
758
|
+
## 许可证
|
|
759
|
+
|
|
760
|
+
MIT License
|