为什么选择 Loguru?
Python 内置的 logging 模块虽然功能完整,但配置繁琐、使用复杂,往往需要大量样板代码。Loguru 是一个让日志处理重新变得简单的第三方库,相比标准库有以下显著优势:
开箱即用:无需任何配置,导入即用,默认就有漂亮的彩色输出
异常追踪:自动记录异常的完整堆栈信息,变量值一目了然
功能强大:支持日志轮转、异步记录、多目标输出、上下文绑定等
无缝集成:可通过
InterceptHandler接管标准库logging,与第三方库完全兼容
1. 安装
使用 pip:
pip install loguru
或使用 uv(推荐):
uv add loguru
2. 基础用法
Loguru 默认就能用,无需任何初始化配置:
from loguru import logger
logger.debug("这是一条调试信息")
logger.info("这是一条普通信息")
logger.warning("这是一条警告信息")
logger.error("这是一条错误信息")
logger.critical("这是一条严重错误信息")
logger.success("操作成功!") # Loguru 独有的 success 级别
3. 异常处理
Loguru 对异常的支持非常强大,能自动打印完整堆栈和变量值:
from loguru import logger
# 方式一:手动记录异常
try:
1 / 0
except Exception:
logger.exception("发生了除零错误") # 自动附加堆栈信息
# 方式二:装饰器自动捕获
@logger.catch
def risky_function():
return 1 / 0
risky_function() # 异常被自动捕获并记录,程序不会崩溃
4. 日志文件与轮转
Loguru 的文件输出配置非常直观,支持按大小、时间自动轮转:
from loguru import logger
import sys
# 移除默认的控制台输出(可选)
logger.remove()
# 重新添加控制台输出,设置级别
logger.add(sys.stderr, level="INFO")
# 添加文件输出,配置轮转和压缩
logger.add(
"logs/app_{time:YYYY-MM-DD}.log", # 按日期命名
rotation="10 MB", # 超过 10MB 自动切换
retention="7 days", # 保留最近 7 天
compression="zip", # 压缩历史日志
level="DEBUG",
encoding="utf-8",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name}:{line} | {message}"
)
5. 格式化与上下文绑定
Loguru 支持结构化日志和上下文信息绑定,非常适合追踪请求链路:
from loguru import logger
# 结构化字段
logger.info("用户 {name} 登录成功,IP: {ip}", name="张三", ip="192.168.1.1")
# 持久绑定上下文(适合同一用户/请求的多条日志)
user_logger = logger.bind(user_id="u001", username="张三")
user_logger.info("开始执行操作")
user_logger.success("操作完成")
# 临时上下文(with 块内有效)
with logger.contextualize(request_id="req-abc123"):
logger.info("处理请求开始")
logger.info("处理请求结束") # 两条日志都会带上 request_id
6. 高级特性
异步日志记录
在异步应用中,使用 enqueue=True 开启非阻塞写入:
from loguru import logger
logger.add("async.log", enqueue=True) # 异步写入,不阻塞主线程
async def handle_request():
logger.info("请求处理中...") # 日志写入不影响响应速度
自定义日志级别
from loguru import logger
# 添加自定义级别(no 为级别数值,介于 DEBUG=10 和 INFO=20 之间)
logger.level("TRACE_CUSTOM", no=15, color="", icon="🔍")
logger.log("TRACE_CUSTOM", "这是自定义级别的日志")
条件日志(环境区分)
from loguru import logger
import os, sys
logger.remove()
is_dev = os.getenv("ENV") == "development"
logger.add(sys.stderr, level="DEBUG" if is_dev else "INFO")
美化输出
from loguru import logger
logger.info("🚀 服务启动成功!")
logger.info("成功 执行了操作")
logger.error("失败 :发生错误")
7. 将标准库 logging 重定向到 Loguru
这是本文的核心主题。当项目中已有大量使用标准库 logging 的第三方库(如 FastAPI、Uvicorn、SQLAlchemy)时,通过 InterceptHandler 可以将它们的日志也统一交由 Loguru 处理。
核心原理
标准库 logging 支持自定义 Handler。在 emit() 方法中:
将
logging的级别名称映射到 Loguru 对应级别向上追踪真实调用栈,确保日志来源文件/行号准确
将异常信息一并传递给 Loguru
完整实现
from loguru import logger
import logging
class InterceptHandler(logging.Handler):
"""将标准库 logging 重定向到 Loguru 的拦截处理器"""
def emit(self, record: logging.LogRecord) -> None:
# 将 logging 级别映射到 Loguru 级别
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
# 追踪真实调用栈,确保文件名和行号准确
frame, depth = logging.currentframe(), 2
while frame.f_code.co_filename == logging.__file__:
frame = frame.f_back
depth += 1
# 转发日志到 Loguru(携带异常信息)
logger.opt(depth=depth, exception=record.exc_info).log(
level, record.getMessage()
)
def setup_logging(level: str = "INFO") -> None:
"""全局配置:将所有 logging 输出重定向到 Loguru"""
logging.root.handlers = []
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
# 拦截常见第三方库的 logger
for name in ["uvicorn", "uvicorn.error", "uvicorn.access", "fastapi", "sqlalchemy"]:
logging.getLogger(name).handlers = [InterceptHandler()]
logging.getLogger(name).propagate = False
在项目中使用
from loguru import logger
import logging
from your_module import setup_logging
# 在应用启动时调用一次即可
setup_logging(level="DEBUG")
# 之后无论是标准库还是 Loguru,输出格式完全统一
logging.getLogger("myapp").info("标准库日志,已被 Loguru 接管")
logging.warning("警告信息,同样走 Loguru")
logger.info("Loguru 原生日志")
8. 与 FastAPI / Uvicorn 集成
FastAPI 和 Uvicorn 内部大量使用标准库 logging,通过以下方式可以将所有日志统一收归 Loguru 管理:
from fastapi import FastAPI
from loguru import logger
import logging
from contextlib import asynccontextmanager
class InterceptHandler(logging.Handler):
def emit(self, record):
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
frame, depth = logging.currentframe(), 2
while frame.f_code.co_filename == logging.__file__:
frame = frame.f_back
depth += 1
logger.opt(depth=depth, exception=record.exc_info).log(level, record.getMessage())
@asynccontextmanager
async def lifespan(app: FastAPI):
# 启动时统一重定向日志
logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)
for name in ["uvicorn", "uvicorn.error", "uvicorn.access"]:
logging.getLogger(name).handlers = [InterceptHandler()]
logging.getLogger(name).propagate = False
logger.info("🚀 日志系统初始化完成")
yield
app = FastAPI(lifespan=lifespan)
@app.get("/")
async def root():
logger.info("处理请求: GET /")
return {"message": "Hello World"}
9. 注意事项
调用时机:
setup_logging()必须在所有第三方库 import 之后、应用启动之前调用,否则部分库的 logger 已完成初始化,拦截可能不完整。propagate 设置:对单独拦截的第三方库 logger,记得将
propagate设为False,避免日志被重复输出。level=0:
logging.basicConfig(level=0)接受所有级别,具体过滤由 Loguru 负责。多进程环境:在 Gunicorn 等多进程场景下,每个子进程需独立初始化,建议在进程启动钩子中调用
setup_logging()。异步场景:在高并发异步应用中,使用
logger.add(..., enqueue=True)开启异步写入,避免 I/O 阻塞影响性能。
总结
Loguru 是 Python 日志处理的终极方案。通过 InterceptHandler 拦截标准库 logging,可以将整个项目(包括所有第三方库)的日志统一交由 Loguru 处理,做到:
✅ 与标准库
logging完全兼容,零侵入改造✅ 享受 Loguru 的彩色输出、异常追踪、文件轮转等现代特性
✅ 统一日志格式,告别多套日志系统并存的混乱局面
如果你的项目还在用裸的 logging,现在就可以迁移了——代价极低,收益显著。