Loguru 是一个非常好用的 Python 日志库。相比标准库 logging,它最大的特点是:
- 配置简单,开箱即用
- 日志格式友好
- 支持文件轮转、压缩、保留策略
- 支持异常堆栈追踪
- 支持结构化上下文
- 支持异步/多进程场景
- 可以方便地拦截标准
logging - 特别适合 Web 服务、脚本、爬虫、数据处理和 CLI 项目
安装
bash
pip install loguru
验证:
bash
python -c "from loguru import logger; logger.info('Hello Loguru')"
输出类似:
text
2026-08-28 15:56:20.123 | INFO | __main__:<module>:1 - Hello Loguru
最基本的使用
Loguru 不需要创建 Logger 实例。
直接:
python
from loguru import logger
logger.debug("这是 debug 日志")
logger.info("程序启动")
logger.warning("这是警告")
logger.error("发生错误")
logger.critical("严重错误")
默认会输出到 stderr。
常用级别:
| Level | 用途 |
|---|---|
TRACE |
非常详细的调试信息 |
DEBUG |
调试信息 |
INFO |
正常运行信息 |
SUCCESS |
成功操作 |
WARNING |
警告 |
ERROR |
普通错误 |
CRITICAL |
严重错误 |
例如:
python
logger.trace("进入函数")
logger.debug("user_id={}", user_id)
logger.info("用户登录成功")
logger.success("订单创建成功")
logger.warning("磁盘空间不足")
logger.error("数据库连接失败")
logger.critical("服务无法启动")
Loguru 最重要的特性:格式化
Loguru 推荐使用 {}:
python
name = "Alice"
age = 20
logger.info("用户 {} 的年龄是 {}", name, age)
输出:
text
用户 Alice 的年龄是 20
也可以使用关键字:
python
logger.info(
"用户 {name} 的年龄是 {age}",
name="Alice",
age=20,
)
不要过度使用 f-string
可以这样:
python
logger.info(f"user_id={user_id}")
但更推荐:
python
logger.info("user_id={}", user_id)
因为 Loguru 自己负责格式化。
支持复杂对象
python
user = {
"id": 1001,
"name": "Alice",
"roles": ["admin", "user"],
}
logger.info("用户信息:{}", user)
添加日志文件
这是实际项目中最常见的需求。
python
from loguru import logger
logger.add("app.log")
logger.info("程序启动")
logger.error("发生错误")
此时日志会同时:
text
终端
+
app.log
日志文件轮转
生产环境千万不要让一个日志文件无限增长。
Loguru 可以:
python
logger.add(
"app.log",
rotation="10 MB",
)
意思是:
日志文件达到 10 MB 后自动创建新文件。
也可以按时间:
python
logger.add(
"app.log",
rotation="00:00",
)
每天零点轮转。
或者:
python
logger.add(
"app.log",
rotation="1 day",
)
常见 rotation
python
rotation="10 MB"
rotation="1 GB"
rotation="1 day"
rotation="1 week"
rotation="00:00"
例如:
python
logger.add(
"logs/app_{time}.log",
rotation="100 MB",
)
日志保留策略
比如只保留最近 30 天:
python
logger.add(
"logs/app.log",
rotation="100 MB",
retention="30 days",
)
或者只保留 10 个日志文件:
python
logger.add(
"logs/app.log",
retention=10,
)
日志压缩
生产环境非常推荐开启压缩:
python
logger.add(
"logs/app.log",
rotation="100 MB",
retention="30 days",
compression="zip",
)
生成的旧日志可能类似:
text
app.log
app.2026-08-28_15-00-00.log.zip
app.2026-08-27_15-00-00.log.zip
支持常见压缩格式,例如:
python
compression="zip"
compression="gz"
compression="tar.gz"
推荐的生产级配置
一个普通 Python 项目可以直接使用:
python
from pathlib import Path
from loguru import logger
LOG_DIR = Path("logs")
LOG_DIR.mkdir(exist_ok=True)
logger.add(
LOG_DIR / "app.log",
rotation="100 MB",
retention="30 days",
compression="zip",
encoding="utf-8",
enqueue=True,
)
其中:
python
enqueue=True
非常值得注意。
它可以让日志写入通过队列处理,在多线程/多进程程序中通常更加安全。
自定义日志格式
例如:
python
logger.add(
"app.log",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
)
输出:
text
2026-08-28 15:20:31 | INFO | 程序启动
2026-08-28 15:20:32 | ERROR | 数据库连接失败
常用 format 字段
text
{time}
{level}
{message}
{name}
{module}
{function}
{line}
{process}
{thread}
{file}
例如:
python
logger.add(
"app.log",
format=(
"{time:YYYY-MM-DD HH:mm:ss.SSS} | "
"{level:<8} | "
"{name}:{function}:{line} | "
"{message}"
),
)
可能得到:
text
2026-08-28 15:30:21.123 | INFO | main:start:20 | 服务启动
彩色日志
终端中可以:
python
logger.info("<green>启动成功</green>")
logger.warning("<yellow>磁盘空间不足</yellow>")
logger.error("<red>连接失败</red>")
也可以使用:
python
logger.add(
"app.log",
colorize=False,
)
通常:
终端可以彩色,文件日志不要彩色。
异常日志:Loguru 的杀手级功能
普通:
python
try:
1 / 0
except Exception as e:
logger.error("发生异常:{}", e)
虽然可以,但堆栈信息不完整。
推荐:
python
try:
1 / 0
except Exception:
logger.exception("计算失败")
输出:
text
ERROR 计算失败
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
logger.exception() 非常重要
例如:
python
def test():
data = {}
return data["name"]
try:
test()
except Exception:
logger.exception("执行 test 失败")
你可以获得完整 traceback。
这比:
python
logger.error(str(e))
好很多。
@logger.catch
Loguru 提供了一个非常方便的装饰器:
python
from loguru import logger
@logger.catch
def divide(a, b):
return a / b
divide(10, 0)
发生异常时 Loguru 会自动记录 traceback。
更适合程序入口
例如:
python
@logger.catch
def main():
run_server()
process_data()
start_worker()
if __name__ == "__main__":
main()
这样没有被捕获的异常也可以自动记录。
控制 catch 是否重新抛出异常
例如:
python
@logger.catch(reraise=True)
def test():
raise ValueError("测试异常")
这样:
- Loguru 记录异常
- 异常继续向上抛
对于很多应用程序来说:
python
@logger.catch(reraise=True)
是比较合理的选择。
日志级别过滤
例如只记录 WARNING 以上:
python
logger.add(
"error.log",
level="WARNING",
)
这样:
python
logger.info("不会写入")
logger.warning("会写入")
logger.error("会写入")
不同日志文件
实际项目可以:
python
logger.add(
"logs/info.log",
level="INFO",
)
logger.add(
"logs/error.log",
level="ERROR",
)
于是:
text
info.log
INFO
WARNING
ERROR
error.log
ERROR
CRITICAL
只记录 ERROR
如果希望严格只记录某一级别,可以使用过滤器:
python
logger.add(
"logs/error.log",
filter=lambda record: record["level"].name == "ERROR",
)
根据模块过滤
例如:
python
logger.add(
"database.log",
filter=lambda record: record["name"] == "database",
)
不过在大型项目中,我更推荐使用 bind() / contextualize() 搭配业务字段。
bind():给日志绑定上下文
这是 Loguru 很实用的功能。
例如一个请求:
python
request_logger = logger.bind(request_id="abc123")
request_logger.info("收到请求")
request_logger.info("开始查询数据库")
request_logger.info("请求处理完成")
日志中可以通过格式显示:
python
logger.add(
"app.log",
format="{time} | request_id={extra[request_id]} | {message}",
)
得到:
text
request_id=abc123 | 收到请求
request_id=abc123 | 开始查询数据库
request_id=abc123 | 请求处理完成
contextualize():临时上下文
例如:
python
with logger.contextualize(request_id="abc123"):
logger.info("开始处理")
logger.info("查询数据库")
logger.info("处理完成")
在这个 with 作用域内,日志都自动带上:
text
request_id=abc123
Web 项目中特别有用
比如:
python
def handle_request(request_id):
with logger.contextualize(request_id=request_id):
logger.info("开始请求")
process()
logger.info("请求完成")
这样你不需要每次:
python
logger.info("request_id={} 开始请求", request_id)
logger.info("request_id={} 查询数据库", request_id)
logger.info("request_id={} 请求完成", request_id)
代码会干净很多。
extra 自定义字段
可以:
python
logger.bind(
user_id=1001,
request_id="abc123",
).info("用户操作")
然后:
python
logger.add(
"app.log",
format=(
"{time} | "
"{level} | "
"{extra} | "
"{message}"
),
)
输出类似:
text
INFO | {'user_id': 1001, 'request_id': 'abc123'} | 用户操作
结构化日志
如果日志最终要进入:
- Elasticsearch
- Loki
- Splunk
- Datadog
- 云日志平台
可以考虑 JSON。
例如:
python
import sys
from loguru import logger
logger.remove()
logger.add(
sys.stdout,
serialize=True,
)
logger.bind(
user_id=1001,
request_id="abc123",
).info("用户登录")
Loguru 会输出 JSON 风格的数据。
这对于日志平台非常方便。
将标准 logging 接入 Loguru
这是实际项目中非常重要的一点。
很多第三方库使用:
python
import logging
logging.getLogger(__name__).info("xxx")
而你的项目使用 Loguru。
可以把标准 logging 转发到 Loguru。
一种常见方式:
python
import logging
import sys
from loguru import logger
class InterceptHandler(logging.Handler):
def emit(self, record):
level = record.levelname
try:
level = logger.level(level).name
except ValueError:
level = "INFO"
logger.opt(depth=6, exception=record.exc_info).log(
level,
record.getMessage(),
)
logging.basicConfig(
handlers=[InterceptHandler()],
level=0,
force=True,
)
这样第三方库的:
python
logging.info(...)
也可以统一进入 Loguru。
FastAPI / Uvicorn 项目
如果你使用 FastAPI,通常会遇到:
text
FastAPI
Uvicorn
standard logging
Loguru
多套日志系统的问题。
可以统一到 Loguru。
例如:
python
from loguru import logger
logger.remove()
logger.add(
"logs/app.log",
rotation="100 MB",
retention="30 days",
compression="zip",
enqueue=True,
encoding="utf-8",
)
然后配置标准 logging 拦截。
这样应用层和很多依赖库的日志就能统一。
多进程
如果你使用:
python
multiprocessing
或者:
text
Gunicorn
Celery
多进程 Worker
建议:
python
logger.add(
"logs/app.log",
enqueue=True,
)
enqueue=True 会通过队列将日志写入串行化,避免多个进程同时写日志造成问题。
异步程序
例如:
python
async def main():
logger.info("开始执行")
await do_something()
logger.info("执行完成")
Loguru 可以正常用于 asyncio。
如果日志量很大,可以考虑:
python
logger.add(
"logs/app.log",
enqueue=True,
)
删除默认 Handler
Loguru 默认已经存在一个 handler。
如果你:
python
logger.add("app.log")
那么实际上是:
text
默认 handler
↓
终端
新增 handler
↓
app.log
如果你想完全自己控制:
python
logger.remove()
然后:
python
logger.add(
sys.stdout,
level="INFO",
)
logger.add(
"logs/app.log",
level="DEBUG",
)
一个比较完整的 logging.py
实际项目我比较推荐把日志配置单独放一个文件:
text
project/
├── app/
│ ├── __init__.py
│ ├── main.py
│ └── service.py
│
├── logs/
│
├── logging_config.py
└── requirements.txt
logging_config.py:
python
import sys
from pathlib import Path
from loguru import logger
LOG_DIR = Path("logs")
LOG_DIR.mkdir(parents=True, exist_ok=True)
def setup_logging():
logger.remove()
# 控制台
logger.add(
sys.stderr,
level="INFO",
format=(
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
"<level>{level: <8}</level> | "
"{name}:{function}:{line} | "
"<level>{message}</level>"
),
)
# 普通日志
logger.add(
LOG_DIR / "app.log",
level="INFO",
rotation="100 MB",
retention="30 days",
compression="zip",
encoding="utf-8",
enqueue=True,
)
# 错误日志
logger.add(
LOG_DIR / "error.log",
level="ERROR",
rotation="50 MB",
retention="30 days",
compression="zip",
encoding="utf-8",
enqueue=True,
)
return logger
然后:
python
# main.py
from loguru import logger
from logging_config import setup_logging
setup_logging()
def main():
logger.info("应用启动")
try:
1 / 0
except Exception:
logger.exception("执行失败")
if __name__ == "__main__":
main()
一个容易踩坑的问题:重复 add()
不要在多个模块里反复:
python
logger.add("app.log")
例如:
python
# a.py
logger.add("app.log")
# b.py
logger.add("app.log")
# c.py
logger.add("app.log")
可能造成:
text
一条日志
↓
写一次
↓
写两次
↓
写三次
正确做法是:
整个应用统一初始化一次 Loguru。
业务模块只:
python
from loguru import logger
然后直接使用。
推荐项目结构
比较成熟的项目:
text
project/
│
├── app/
│ ├── __init__.py
│ ├── main.py
│ ├── api/
│ ├── services/
│ ├── models/
│ └── utils/
│
├── config/
│ └── settings.py
│
├── logs/
│ ├── app.log
│ └── error.log
│
├── core/
│ └── logging.py
│
└── requirements.txt
core/logging.py:
python
import sys
from pathlib import Path
from loguru import logger
def setup_logging():
log_dir = Path("logs")
log_dir.mkdir(exist_ok=True)
logger.remove()
logger.add(
sys.stderr,
level="INFO",
)
logger.add(
log_dir / "app.log",
rotation="100 MB",
retention="30 days",
compression="zip",
enqueue=True,
encoding="utf-8",
)
logger.add(
log_dir / "error.log",
level="ERROR",
rotation="100 MB",
retention="90 days",
compression="zip",
enqueue=True,
encoding="utf-8",
)
然后应用入口:
python
from core.logging import setup_logging
setup_logging()
业务代码:
python
from loguru import logger
def create_order(order_id):
logger.info("创建订单 {}", order_id)
try:
...
except Exception:
logger.exception(
"创建订单失败,order_id={}",
order_id,
)
raise
日志设计建议
实际开发中,不要把 Loguru 当成 print()。
不推荐:
python
logger.info("开始")
logger.info("1")
logger.info("2")
logger.info("3")
logger.info("结束")
日志太碎。
推荐:
python
logger.info(
"订单创建成功,order_id={},user_id={}",
order_id,
user_id,
)
让一条日志表达一个有意义的事件。
推荐日志内容
例如:
text
用户登录成功
订单创建成功
支付成功
数据库连接失败
Redis 连接超时
任务执行完成
文件上传失败
HTTP 请求异常
而不是:
text
进入函数
变量是 xxx
执行到这里
if 成立
后者属于 Debug 日志。
INFO / DEBUG / WARNING / ERROR 怎么区分?
可以简单记:
text
DEBUG
↓
开发人员调试程序
INFO
↓
程序正常发生的重要事件
WARNING
↓
程序还能继续运行,但需要注意
ERROR
↓
某个操作失败
CRITICAL
↓
整个系统可能无法继续工作
例如:
python
logger.debug("SQL 参数:{}", params)
logger.info("用户登录成功 user_id={}", user_id)
logger.warning("Redis 响应时间过长:{} ms", elapsed)
logger.error("订单支付失败 order_id={}", order_id)
logger.critical("数据库主库无法连接")
性能方面
如果日志很多:
python
logger.debug("data={}", huge_data)
Loguru 支持 lazy=True:
python
logger.opt(lazy=True).debug(
"data={}",
lambda: expensive_operation(),
)
只有 DEBUG 日志实际需要输出时,才执行:
python
expensive_operation()
对于计算昂贵的日志内容比较有用。
logger.opt() 常用功能
异常
python
logger.opt(exception=True).error("发生错误")
lazy
python
logger.opt(lazy=True).debug(
"result={}",
lambda: expensive_operation(),
)
depth
python
logger.opt(depth=1).info("hello")
在封装日志函数时比较有用,可以让 Loguru 显示真正调用日志的代码位置。
自定义日志函数
例如你不希望业务代码到处出现复杂格式:
python
def log_request(method, path, status):
logger.info(
"HTTP {} {} status={}",
method,
path,
status,
)
使用:
python
log_request("GET", "/api/users", 200)
如果使用这种封装,推荐:
python
logger.opt(depth=1).info(...)
避免日志显示的是 log_request() 的位置,而不是业务调用位置。
Loguru 和标准 logging 对比
| 功能 | logging | Loguru |
|---|---|---|
| 开箱即用 | ❌ | ✅ |
| 简单配置 | ❌ | ✅ |
| 文件轮转 | ✅ | ✅ |
| 压缩 | 需额外处理 | ✅ |
| 异常追踪 | 一般 | ⭐⭐⭐⭐⭐ |
| 上下文 | 较繁琐 | ✅ |
| 彩色日志 | 需配置 | ✅ |
| JSON | 需配置 | ✅ |
| 多进程 | 配置较复杂 | enqueue=True |
| 第三方库兼容 | 原生 | 可拦截 |
| 标准库生态 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
如果是新项目:
Loguru 很值得直接使用。
如果是大型企业项目:
也可以继续使用标准
logging,尤其当项目已经有完善的 logging 基础设施时。
我比较推荐的生产配置
如果你只是想找一个可以直接复制到项目里使用的方案,可以从这个开始:
python
import sys
from pathlib import Path
from loguru import logger
def setup_logging():
log_dir = Path("logs")
log_dir.mkdir(parents=True, exist_ok=True)
logger.remove()
# Console
logger.add(
sys.stderr,
level="INFO",
format=(
"{time:YYYY-MM-DD HH:mm:ss.SSS} | "
"{level:<8} | "
"{name}:{function}:{line} | "
"{message}"
),
)
# Application log
logger.add(
log_dir / "app.log",
level="INFO",
rotation="100 MB",
retention="30 days",
compression="zip",
encoding="utf-8",
enqueue=True,
)
# Error log
logger.add(
log_dir / "error.log",
level="ERROR",
rotation="100 MB",
retention="90 days",
compression="zip",
encoding="utf-8",
enqueue=True,
)
return logger
入口:
python
from loguru import logger
from core.logging import setup_logging
setup_logging()
@logger.catch(reraise=True)
def main():
logger.info("应用启动")
# 业务逻辑
logger.info("开始执行任务")
...
if __name__ == "__main__":
main()
业务模块只需要:
python
from loguru import logger
def process(user_id):
logger.info("开始处理 user_id={}", user_id)
try:
...
except Exception:
logger.exception(
"处理失败 user_id={}",
user_id,
)
raise
核心原则可以浓缩成 5 条:
- 入口统一初始化一次
logger - 业务模块直接
from loguru import logger - 异常优先用
logger.exception() - 生产环境开启
rotation + retention + compression - 多线程/多进程场景开启
enqueue=True