An enhanced logger based on Loguru
Project description
YydsLogger
基于 Loguru 的增强日志记录器,提供稳定的本地日志写入、多语言、异步队列、结构化输出和生命周期管理能力。
特性
- 多语言输出(zh/en)
- 所有运行时中英文提示集中维护在
yyds_logger/i18n.py - 自定义格式、级别过滤、按文件大小轮转、保留策略、gzip 压缩
- 多实例物理隔离:底层内聚了完全独立的
yyds_loguru引擎,彻底打破全局单例限制。每个YydsLogger实例拥有独立的 Core,且contextualize()上下文绑定同样物理隔离,互不串扰。 - JSON 结构化输出(serialize):直接对接 ELK / Loki / Datadog / CloudWatch
- 接管标准库 logging:把 uvicorn / sqlalchemy 等三方库日志统一汇入本管道(通过
sys._getframeC 级原生帧跳过深度优化性能) - 环境感知(env='prod'/'dev'):默认为
'prod'(生产环境优先),生产自动关闭 diagnose/backtrace,使用异步文件写入 - 有界本地队列:支持
queue_size、block/drop溢出策略和queue_timeout - 按 sink 独立级别:控制台、主文件可各自设级别;错误文件固定记录 ERROR 及以上
- request_id 上下文注入(ContextVar)+
bind/contextualize结构化字段与 trace 关联 - 装饰器记录函数调用与耗时(同步/异步),级别关闭时自动跳过昂贵格式化
- 轻量基础统计(总量、级别计数、错误率)
- 可选健康检查(磁盘、内存、日志文件数量和大小)
- 纯零核心依赖:移除了对外部
loguru包的硬性安装依赖,基础版零额外依赖,轻量可靠且彻底杜绝依赖版本冲突风险 - 优雅资源管理:支持
flush()、flush_async()、close()、cleanup()和可选 SIGTERM 处理
安装
pip install -U yyds-logger
核心安装为纯零额外依赖(移除了对外部 loguru 的强制依赖,无版本冲突风险)。按需安装可选能力:
pip install "yyds-logger[profile]" # 行级耗时分析(line_profiler,更精确)
pip install "yyds-logger[monitoring]" # 系统指标与健康检查(psutil)
pip install "yyds-logger[all]" # 一次性安装全部可选依赖
未装
line_profiler时,time_it(line_by_line=True)自动回退到原生sys.settrace。
快速开始
基本使用
from yyds_logger import YydsLogger
# 创建日志记录器实例
logger = YydsLogger(
file_name="app",
log_dir="logs",
language="zh" # 使用中文输出
)
# 设置 request_id(每条日志会带 ReqID:xxx)
token = logger.request_id_var.set("req-001")
try:
logger.info("这是一条信息日志")
logger.warning("这是一条警告日志")
logger.error("这是一条错误日志")
finally:
logger.request_id_var.reset(token)
# 程序退出前清理(排空本地队列、恢复 hook、移除 handler)
logger.cleanup()
需要在程序继续运行时仅排空异步队列,可调用 logger.flush();完全关闭并释放资源可调用
logger.close(),它是 cleanup() 的明确别名。
在已有 asyncio 事件循环的协程中使用:
await logger.flush_async()
request_id 用法与并发说明
request_id_var 是一个 ContextVar,用来给“同一条业务链路”的日志自动带上 ReqID:...:
set(value):把当前上下文的 request_id 设为value,并返回一个 token(表示 set 之前的旧值)reset(token):把当前上下文恢复到 set 之前的值,避免 request_id 泄漏到下一次请求/任务
推荐写法用 try/finally 确保一定 reset(如上例)。
多并发下的行为:
- 多线程:不同线程之间的
request_id互不影响;但新线程不会自动继承父线程的 request_id,需要显式传递。 - asyncio:不同 Task 之间的
request_id互不影响;创建 Task 时会复制一份当前上下文,所以要在create_task()之前 set。
线程/线程池中传递 request_id 的示例:
import contextvars
import threading
token = logger.request_id_var.set("req-001")
try:
ctx = contextvars.copy_context()
t = threading.Thread(target=lambda: ctx.run(logger.info, "子线程日志也带 ReqID"))
t.start()
t.join()
finally:
logger.request_id_var.reset(token)
asyncio 并发示例(每个请求独立 request_id):
import asyncio
async def handle(req_id: str):
token = logger.request_id_var.set(req_id)
try:
logger.info("开始处理")
await asyncio.sleep(0.1)
logger.info("处理完成")
finally:
logger.request_id_var.reset(token)
async def main():
await asyncio.gather(handle("req-1"), handle("req-2"))
asyncio.run(main())
生产环境推荐配置(env)
默认环境为 'prod'。生产模式会关闭 diagnose/backtrace(避免在日志里泄漏变量值并降低开销),
同时保持 enqueue=True(异步文件写入):
logger = YydsLogger(
file_name="app",
log_dir="logs",
# env="prod", # 默认即为 "prod",生产关闭诊断回溯,使用异步文件写入
serialize=True, # 文件输出 JSON,便于日志平台采集
)
多进程配置
多进程部署必须使用 process_isolation=True,让不同进程写入独立的 PID 日志文件,避免轮转和写入竞争;默认值为 False。
本地文件 sink 的 enqueue 队列可通过 queue_size、overflow_policy 和 queue_timeout 配置;
队列满时可选择阻塞或丢弃。默认 block + queue_timeout=None 会在队列满时等待,因此异步写入
不等同于永不阻塞。可通过 get_queue_dropped() 或 get_queue_status() 查看丢弃数量和当前策略。
如需精细控制,可用 enqueue / diagnose / backtrace 三个可选参数显式覆盖:
logger = YydsLogger("app", env="prod", diagnose=True) # 临时排障:单独打开 diagnose
JSON 结构化日志(serialize)
容器 / k8s / 日志平台(ELK、Loki、Datadog)更适合消费 JSON。serialize 控制文件输出,
console_serialize 控制控制台输出:
logger = YydsLogger(
file_name="app",
serialize=True, # 文件写 JSON(每行一条)
console_serialize=False, # 控制台仍保留彩色可读格式
)
logger.info("用户登录")
# 文件中为一行 JSON,包含 time/level/message/extra(含 request_id 及 bind/contextualize 字段)
注意:序列化模式下日志为 JSON,自定义彩色
format的颜色不再生效(这是 loguru 序列化语义)。
结构化上下文与 trace 关联(bind / contextualize)
配合 serialize=True,绑定的字段会自动出现在 JSON 中,方便与 OpenTelemetry / 链路追踪对齐:
# 永久绑定,返回带上下文的 logger
logger.bind(service="order", trace_id="abc123").info("处理下单")
# with 作用域内临时注入(线程/协程安全)
with logger.contextualize(trace_id="abc123", span_id="01"):
logger.info("查询库存")
logger.info("扣减库存")
# request_id 便捷方法
token = logger.set_request_id("req-001")
logger.info(logger.get_request_id())
接管标准库 logging
把三方库(uvicorn、sqlalchemy、requests 等)通过标准库 logging 输出的日志统一汇入本管道:
logger = YydsLogger("app")
# 按需接管指定 logger;不传 names 时接管 root logger。
logger.capture_std_logging(level="INFO", names=["uvicorn", "sqlalchemy.engine"])
默认不会删除目标 logger 已有的 handlers;如需完全接管,可显式传入
clear_existing=True。cleanup() 时会自动恢复被修改的标准库 logging 状态。
未处理异常
如需把主线程和子线程的未处理异常写入日志,可在实例创建后显式安装 hook:
logger = YydsLogger("app")
logger.setup_exception_handler()
异常 hook、标准库 logging 接管和信号处理器均属于进程全局资源;同一时间只能由一个
YydsLogger 实例接管,cleanup() 会恢复本实例仍拥有的原处理器。
按 sink 设置独立级别
控制台、主文件可分别设级别(例如控制台只看 WARNING,文件留全量 DEBUG);错误日志始终写入
{file_name}_error.log,并固定记录 ERROR 及以上级别:
默认不设置 console_level 和 file_level 时,主文件与控制台不进行级别过滤。
logger = YydsLogger(
file_name="app",
console_level="WARNING",
file_level="DEBUG",
)
优雅退出(SIGTERM)
容器 / k8s 用 SIGTERM 停服时 atexit 不一定触发,开启后会在退出前排空 enqueue 队列,避免丢日志:
logger = YydsLogger("app")
logger.setup_signal_handlers() # 拦截并链回原 SIGTERM/SIGINT 处理器
异步函数支持
import asyncio
@logger.log_decorator()
async def async_function():
await asyncio.sleep(1)
return "异步操作完成"
# 使用异步函数
async def main():
result = await async_function()
logger.info(f"异步函数结果: {result}")
asyncio.run(main())
增强错误信息
# 错误日志现在会显示详细的位置信息
@logger.log_decorator("除零错误", level="ERROR")
def divide_numbers(a, b):
return a / b
try:
result = divide_numbers(1, 0)
except ZeroDivisionError:
logger.exception("捕获到除零错误")
# 输出示例:
# 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
# 调用链: app.py:25:divide_numbers -> main.py:10:main
log_decorator() 的 trace 控制详细异常栈;reraise 控制异常是否继续抛出,默认值为
True,不会因关闭详细堆栈而吞掉业务异常:
@logger.log_decorator(trace=False, reraise=True)
def still_raise():
raise ValueError("业务异常仍交给调用方处理")
耗时统计与行分析 (@time_it)
提供 @logger.time_it 装饰器,不仅可以记录函数的整体执行耗时,还能开启行级性能分析,扒开函数内部看哪一行代码在拖后腿(支持同步和异步函数)。
import time
import asyncio
# 1. 最简使用:只查看耗时,不带括号直接装饰(每次都打印耗时)
@logger.time_it
def my_task():
time.sleep(0.05)
my_task()
# 输出示例:
# 2026-05-25 15:37:14.790 | INFO | ReqID:no-request-id | yyds_logger.py:778 | [TIMER] 函数 `my_task` 执行完毕 | 总耗时: 52.15ms
# 2. 进阶版:开启行级性能分析(支持可选依赖 line_profiler,未安装时自动降级为原生 sys.settrace)
@logger.time_it(line_by_line=True)
def heavy_task():
a = 1 + 2
time.sleep(0.02) # 耗时较短
b = a * 3
time.sleep(0.08) # 耗时最长,被标记为瓶颈
return b
heavy_task()
# 输出示例:
# ================================================================================
# [FN-TIMER] 行性能分析报告 -> 函数: `heavy_task`
# ================================================================================
# 行号 | 命中次数 | 总耗时 (ms) | 每步耗时 (ms) | 耗时占比 | 源代码
# --------------------------------------------------------------------------------
# 45 | 1 | 0.00 | 0.00 | 0.0% | a = 1 + 2
# 46 | 1 | 25.05 | 25.05 | 22.8% | time.sleep(0.02) # 这行稍慢 <-- 🚨 性能瓶颈
# 47 | 1 | 0.00 | 0.00 | 0.0% | b = a * 3
# 48 | 1 | 84.66 | 84.66 | 77.1% | time.sleep(0.08) # 这行最慢,应当被标记为瓶颈 <-- 🚨 性能瓶颈
# 49 | 1 | 0.00 | 0.00 | 0.0% | return b
# --------------------------------------------------------------------------------
# 总耗时: 109.78 毫秒
# ================================================================================
健康检查(可选)
健康检查依赖 psutil,可通过可选依赖安装:
pip install yyds-logger[monitoring]
from yyds_logger import LogHealthChecker
checker = LogHealthChecker()
health = checker.check_health("logs")
print(health["status"])
print(health["metrics"]["disk_usage_percent"])
# 如需同时查看 logger 生命周期、handler 数量和队列丢弃数:
print(logger.get_health())
日志统计功能
# 启用统计功能
logger = YydsLogger(
file_name="app",
enable_stats=True
)
# 获取统计信息
stats = logger.get_stats()
print(stats)
高级配置
完整配置示例
logger = YydsLogger(
file_name="app", # 日志文件名
log_dir="logs", # 日志目录
max_size=10, # 单个日志文件最大大小(MB)
retention="7 days", # 日志保留时间
language="zh", # 日志语言(zh/en)
custom_format=None, # 自定义日志格式
compression="gz", # 日志压缩格式,默认 gzip
enable_stats=False, # 是否启用统计
# —— 新增参数 ——
env="prod", # "dev"/"prod"
enqueue=None, # 显式覆盖 enqueue(异步文件写入)
diagnose=None, # 显式覆盖 diagnose
backtrace=None, # 显式覆盖 backtrace
serialize=False, # 文件输出 JSON 结构化日志
console_serialize=False, # 控制台输出 JSON 结构化日志
console_level=None, # 控制台独立级别(默认不过滤)
file_level=None, # 主文件独立级别(默认不过滤)
queue_size=10000, # 本地 enqueue 队列容量,None 表示无界
overflow_policy="block", # 队列满时 block 或 drop
queue_timeout=None, # block 模式最大等待时间
process_isolation=False, # 多进程部署时必须改为 True
)
保留与压缩
retention 只清理当前 sink 的轮转归档,不会删除正在写入的活动文件,也不会扫描另一类
sink 的文件:
# 默认:轮转归档为 .log.gz,保留策略作用于这些压缩归档
YydsLogger("app", retention="7 days", compression="gz")
# 不压缩:轮转归档保持为普通 .log,保留策略作用于这些普通归档
YydsLogger("app", retention="7 days", compression=None)
保留清理由下一次日志轮转触发;如果一直没有新的轮转,过期归档会保留到下一次轮转时再清理。
自定义日志格式
custom_format = (
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<level>{level: <8}</level> | "
"ReqID:{extra[request_id]} | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
"<level>{message}</level>"
)
logger = YydsLogger(
file_name="app",
custom_format=custom_format
)
主要功能
1. 日志记录
- 支持所有标准日志级别(DEBUG, INFO, WARNING, ERROR, CRITICAL)
- 支持
log()通用级别入口 - 支持 request_id、bind/contextualize 结构化上下文
2. 日志管理
- 自动日志轮转
- gzip 日志压缩
- 日志保留策略
- 固定 ERROR 及以上级别的独立错误日志文件
- 本地 enqueue 队列与溢出策略
3. 统计功能
- 日志总数统计
- 错误率统计
- DEBUG/INFO/WARNING/ERROR 级别计数
- 统计开关关闭时不产生统计开销
4. 装饰器支持
- 函数执行时间记录
- 异常捕获和记录
- 支持同步和异步函数
5. 增强错误信息
- 显示错误发生的具体文件、行号和函数名
- 显示错误发生时的代码行内容
- 显示调用链信息(最后3层调用)
- 支持全局异常处理器
6. 性能优化
- 级别门控减少无效日志构造
- 可选 enqueue 异步文件写入
- 有界队列支持阻塞或丢弃策略
- 行级性能分析作为独立可选能力
7. 运维能力
- 可选接管标准库
logging - 可选异常钩子和 SIGTERM/SIGINT 生命周期处理
- 可选健康检查:磁盘、内存和日志文件状态
错误处理
try:
logger = YydsLogger("app", log_dir="/path/to/logs")
except RuntimeError as e:
print(f"日志配置失败: {e}")
注意事项
- 确保日志目录具有写入权限
- 生产环境建议使用
env="prod"+serialize=True,并按需调用setup_signal_handlers() - 异步操作时注意正确处理异常
serialize=True时日志为 JSON,自定义彩色format的颜色不再生效YydsLogger实例的日志 sink 相互隔离;但标准库 logging、异常 hook 和信号处理器属于进程全局资源,同一时间只能由一个实例接管
贡献
欢迎提交 Issue 和 Pull Request!
许可证
MIT License
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yyds_logger-0.5.1.tar.gz.
File metadata
- Download URL: yyds_logger-0.5.1.tar.gz
- Upload date:
- Size: 92.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
251b873c2bbb91c6cd966f08b0bd2e345cb1933743bad8c67f914a623d951c0f
|
|
| MD5 |
a44264f2e98f1e297363800e769423b5
|
|
| BLAKE2b-256 |
37e9dccc3567c493ffbf0a2d3ab61ac4e663553d93e1267333de04a132ac15ab
|
File details
Details for the file yyds_logger-0.5.1-py3-none-any.whl.
File metadata
- Download URL: yyds_logger-0.5.1-py3-none-any.whl
- Upload date:
- Size: 86.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91d2f31b715038d1a7aea645371cc2ef98d6b78604e5dd64489b86c43e1be389
|
|
| MD5 |
3bc992045c8e5df97be488de17234220
|
|
| BLAKE2b-256 |
bbec469c637cc40e79154d1b6d7f89a849042f34029cbf50d49394319804a753
|