Skip to main content

YydsLogger

基于 Loguru 的增强日志记录器,提供稳定的本地日志写入、多语言、异步队列、结构化输出和生命周期管理能力。

特性

  • 多语言输出(zh/en)
  • 所有运行时中英文提示集中维护在 yyds_logger/i18n.py
  • 自定义格式、级别过滤、按文件大小轮转、保留策略、gzip 压缩
  • 多实例物理隔离:底层内聚了完全独立的 yyds_loguru 引擎,彻底打破全局单例限制。每个 YydsLogger 实例拥有独立的 Core,且 contextualize() 上下文绑定同样物理隔离,互不串扰。
  • JSON 结构化输出(serialize):直接对接 ELK / Loki / Datadog / CloudWatch
  • 接管标准库 logging:把 uvicorn / sqlalchemy 等三方库日志统一汇入本管道(通过 sys._getframe C 级原生帧跳过深度优化性能)
  • 环境感知(env='prod'/'dev'):默认为 'prod' (生产环境优先),生产自动关闭 diagnose/backtrace,使用异步文件写入
  • 有界本地队列:支持 queue_size、block/drop 溢出策略和 queue_timeout
  • writer 侧延迟格式化:线程队列可用 defer_format=True 将格式化、异常渲染和 JSON 序列化移到 writer,降低业务线程日志延迟
  • 按 sink 独立级别:控制台、主文件可各自设置级别
  • request_id 上下文注入(ContextVar)+ bind/contextualize 结构化字段与 trace 关联
  • 装饰器记录函数调用与耗时(同步/异步),级别关闭时自动跳过昂贵格式化
  • 轻量基础统计(总量、级别计数、错误率)
  • 可选健康检查(磁盘、内存、日志文件数量和大小)
  • 纯零核心依赖:移除了对外部 loguru 包的硬性安装依赖,基础版零额外依赖,轻量可靠且彻底杜绝依赖版本冲突风险
  • 优雅资源管理:支持 flush()、flush_async()、close()、aclose()、同步/异步上下文管理和可选 SIGTERM 处理

安装

需要 Python 3.9 或更高版本。

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()
await logger.aclose()

# 也可以自动异步关闭
async with YydsLogger("app") as logger:
    logger.info("处理中")

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。对于 Gunicorn、uWSGI 等 pre-fork 模式,即使 logger 在 master 中创建,worker 也会在 fork 后自动重建自己的 sink 并使用 worker PID 文件名。使用 spawn 模式时请在子进程内创建 logger,不要将实例作为进程参数传递。

本地文件 sink 的 enqueue 队列可通过 queue_size、overflow_policy、queue_timeout、 queue_backend 和 shutdown_timeout 配置。队列满时可选择阻塞或丢弃;默认 block 最多等待 1 秒,flush/close 的每次队列控制或 writer join 默认最多等待 30 秒。将 shutdown_timeout 设为 None 可恢复无限等待。

当格式化本身(动态 format、异常回溯或 JSON 序列化)较重时,可对本地文件 sink 打开 defer_format=True。它要求 enqueue=True 且 queue_backend="thread",日志调用只复制当时的 record 快照并入队,实际格式化由 writer 执行;因此动态 format 函数的执行线程和执行时机也会改变。

queue_backend="auto" 默认使用进程内线程队列,避免 pickle、IPC、额外 feeder 线程和文件描述符开销。 多进程部署配合 process_isolation=True 时,每个 worker 同样使用自己的线程队列和 PID 文件。 只有确实依赖 multiprocessing 队列语义时才应显式选择 "multiprocessing";不可 pickle 的结构化字段 会被拒绝并计入 dropped_messages 与 serialization_errors,可通过 get_queue_status() 查看。

运行中可读取队列深度、容量、利用率、writer 状态和各类丢弃原因:

status = logger.get_queue_status()
print(status["depth"], status["utilization"], status["drop_reasons"])

需要切换格式、级别或队列策略时,可用 reconfigure() 原子替换本实例管理的 sinks;失败时旧配置会继续生效:

logger.reconfigure(file_level="DEBUG", defer_format=True)

如需精细控制,可用 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):

默认不设置 console_level 和 file_level 时,主文件与控制台不进行级别过滤。

logger = YydsLogger(
    file_name="app",
    console_level="WARNING",
    file_level="DEBUG",
)

优雅退出(SIGTERM)

容器 / k8s 用 SIGTERM 停服时 atexit 不一定触发,开启后信号处理器只发送通知,后台清理线程 负责在有界超时内排空 enqueue 队列,避免在 Python 信号上下文中执行文件 I/O:

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=1.0,                  # block 模式最大等待时间;None 表示无限等待
    queue_backend="auto",              # 默认解析为 thread
    shutdown_timeout=30.0,              # 队列控制或 writer join 的单次等待上限
    defer_format=False,                 # 在线程队列 writer 侧执行格式化/序列化
    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 日志压缩
  • 日志保留策略
  • 本地 enqueue 队列与溢出策略

3. 统计功能

  • 日志总数统计
  • 错误率统计
  • DEBUG/INFO/WARNING/ERROR 级别计数
  • 统计开关关闭时不产生统计开销

4. 装饰器支持

  • 函数执行时间记录
  • 异常捕获和记录
  • 支持同步和异步函数

5. 增强错误信息

  • 显示错误发生的具体文件、行号和函数名
  • 显示错误发生时的代码行内容
  • 显示调用链信息(最后3层调用)
  • 支持全局异常处理器

6. 性能优化

  • 级别门控减少无效日志构造
  • 可选 enqueue 异步文件写入
  • 有界队列支持阻塞或丢弃策略
  • 行级性能分析作为独立可选能力

7. 运维能力

  • 可选接管标准库 logging
  • 可选异常钩子和 SIGTERM/SIGINT 生命周期处理
  • 可选健康检查:磁盘、内存和日志文件状态

性能基准

仓库提供零额外依赖的基准工具,用于比较同步写入、异步阻塞队列和有界丢弃队列的生产端吞吐、 P50/P95/P99 延迟、排空时间与丢弃量:

python benchmarks/bench_logging.py --mode all --messages 20000 --threads 4
python benchmarks/bench_logging.py --mode enqueue --serialize --json

基准会使用临时日志目录,不会污染项目目录;结果受磁盘、CPU、Python 版本与并发负载影响,应在目标部署环境中比较。

错误处理

try:
    logger = YydsLogger("app", log_dir="/path/to/logs")
except RuntimeError as e:
    print(f"日志配置失败: {e}")

注意事项

  1. 确保日志目录具有写入权限
  2. 生产环境建议使用 env="prod" + serialize=True,并按需调用 setup_signal_handlers()
  3. 异步操作时注意正确处理异常
  4. serialize=True 时日志为 JSON,自定义彩色 format 的颜色不再生效
  5. YydsLogger 实例的日志 sink 相互隔离;但标准库 logging、异常 hook 和信号处理器属于进程全局资源,同一时间只能由一个实例接管

贡献

欢迎提交 Issue 和 Pull Request!

许可证

MIT License

本发行版内置了经修改的 Loguru 引擎;完整归属与许可证说明见 THIRD_PARTY_NOTICES.md。

Metadata

Release files for yyds-logger 0.8.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for yyds-logger 0.8.1
File Size Uploaded
yyds_logger-0.8.1.tar.gz 112.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for yyds-logger 0.8.1
File Interpreter ABI Platform
yyds_logger-0.8.1-py3-none-any.whl Python 3 none any Details

Total release size: 214.8 kB

Release files / yyds_logger-0.8.1.tar.gz

Download URL yyds_logger-0.8.1.tar.gz
Size 112.3 kB
Tags Source
SHA-256 checksum
How to use checksums
48c09b3c6fef183994f2afe182913162e9f56e8a138799dc22fc079ecd66917c
BLAKE2b-256 checksum
How to use checksums
2e96a4fd1f66472ec9ced3d63e386385c2e7edc0e350697de2470103c66c5953
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.11

Release files / yyds_logger-0.8.1-py3-none-any.whl

Download URL yyds_logger-0.8.1-py3-none-any.whl
Size 102.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d34331d0fcfbf73c3495334a50ff82ca9eb6a5df5c7b3a2af698771d4a6fab7b
BLAKE2b-256 checksum
How to use checksums
e4c546f778af2152fbf99f3a9fa1ed8234bffdab50cb9dca8694af81cf31fad3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.11

Release history Release notifications | RSS feed

This release

0.8.1 This release

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page