Loguru 以其开箱即用
的特性和简洁的 API 设计,在 Python 开发者中广受欢迎。相比于 Python 内置的 logging
模块,Loguru 在很多场景下能让日志记录变得更加简单和直观
使用场景
- 替代 print() 进行调试: 在开发过程中,我们经常使用
print()
输出变量或程序状态。但这种方式在代码发布后通常需要手动移除,且无法控制输出级别。Loguru 提供了更灵活的方式,可以轻松切换日志级别,在生产环境中自动关闭调试信息,同时保留错误记录。
- Web 开发后端日志: 在如 Flask, Django, FastAPI 等 Web 框架中,记录请求信息、数据库查询、业务逻辑处理流程和异常捕获至关重要。Loguru 可以轻松地将日志输出到文件,并按日期、大小自动分割(Rotating Files),方便排查线上问题。
- 数据科学与机器学习: 在长时间运行的数据处理或模型训练任务中,记录关键步骤、中间结果、性能指标和可能出现的错误非常有用。通过 Loguru,可以清晰地追踪任务执行进度,方便复现和分析。
- 命令行工具 (CLI): 为命令行工具添加日志功能,可以向用户展示程序执行的详细过程,或者将错误信息记录到文件中供开发者分析。Loguru 可以同时向控制台和文件输出不同级别的日志,非常方便。
- 异步编程 (Asyncio): Loguru 对异步代码有良好的支持,可以安全地在
async/await
代码中记录日志,而不会引发线程安全问题。
- 结构化日志 (Structured Logging): Loguru 可以方便地输出 JSON 格式的日志。这对于将日志发送到集中式日志管理平台(如 ELK Stack, Splunk, Datadog)进行分析、监控和告警非常关键。
安装 Loguru
Loguru 核心用法与示例
下面我们将通过具体的代码示例来展示 Loguru 的核心功能。
1. 基础使用:替代 print
最简单的用法是直接导入 logger
对象并使用。
from loguru import logger
# Loguru 会默认向 sys.stderr 输出日志,级别为 DEBUG 及以上
logger.debug("这是一个调试信息,通常用于开发阶段。")
logger.info("程序正在正常运行...")
logger.success("任务成功完成!") # success 是 Loguru 特有的级别
logger.warning("注意!检测到一个潜在问题。")
logger.error("发生了一个错误,但不影响程序继续运行。")
logger.critical("发生了严重错误,程序可能即将崩溃!")
# 记录异常信息
try:
result = 1 / 0
except ZeroDivisionError:
logger.exception("计算失败,捕获到一个异常:")
输出结果:
2025-05-02 22:40:59.287 | DEBUG | __main__:<module>:4 - 这是一个调试信息,通常用于开发阶段。
2025-05-02 22:40:59.288 | INFO | __main__:<module>:5 - 程序正在正常运行...
2025-05-02 22:40:59.288 | SUCCESS | __main__:<module>:6 - 任务成功完成!
2025-05-02 22:40:59.288 | WARNING | __main__:<module>:7 - 注意!检测到一个潜在问题。
2025-05-02 22:40:59.288 | ERROR | __main__:<module>:8 - 发生了一个错误,但不影响程序继续运行。
2025-05-02 22:40:59.288 | CRITICAL | __main__:<module>:9 - 发生了严重错误,程序可能即将崩溃!
2025-05-02 22:40:59.288 | ERROR | __main__:<module>:15 - 计算失败,捕获到一个异常:
Traceback (most recent call last):
> File "example.py", line 13, in <module>
result = 1 / 0
ZeroDivisionError: division by zero
2. 添加与配置 Sink:输出到文件
Loguru 使用 add()
方法来配置日志的输出目的地,称之为 “Sink”。你可以添加多个 Sink。
import sys
from loguru import logger
# 移除默认的控制台输出
logger.remove()
# 添加一个新的控制台输出,并设置日志级别为 INFO
logger.add(sys.stderr, level="INFO")
# 添加一个日志文件,每天午夜创建一个新文件,并自动压缩旧文件
logger.add("logs/file_{time:YYYY-MM-DD}.log",
rotation="00:00", # 每天 00:00 创建新文件
retention="10 days", # 最多保留 10 天的日志
compression="zip", # 使用 zip 格式压缩
level="DEBUG", # 文件中记录 DEBUG 及以上级别的日志
encoding="utf-8")
logger.info("这条信息会同时输出到控制台和文件。")
logger.debug("这条信息只会输出到文件,因为控制台级别是 INFO。")
说明:
logger.remove()
可以移除之前配置的 Sink。如果不带参数,则移除所有。
add()
的第一个参数是 Sink,可以是文件路径、sys.stdout
等。
rotation
: 设置日志文件的分割策略,可以是文件大小("500 MB"
)或时间("1 week"
)。
retention
: 设置日志文件的保留策略。
compression
: 设置旧日志文件的压缩格式。
通过 format
参数,你可以完全自定义日志的格式。
from loguru import logger
import sys
# 移除默认配置
logger.remove()
# 定义你自己的格式
log_format = (
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
"<level>{message}</level>"
)
# 添加到控制台,并应用新格式
logger.add(sys.stderr, format=log_format, colorize=True, level="INFO")
# 添加到文件,不需要颜色
logger.add("logs/formatted.log", format=log_format, colorize=False, level="DEBUG")
def my_function():
logger.info("这是一个自定义格式的日志。")
logger.debug("这是另一条调试信息。")
my_function()
控制台输出示例:
2025-05-02 10:35:00.125 | INFO | __main__:my_function:22 - 这是一个自定义格式的日志。
常用格式变量:
{time}
: 时间
{level}
: 日志级别
{message}
: 日志消息
{name}
, {function}
, {line}
: 记录日志所在的文件名、函数名、行号
{process}
, {thread}
: 进程和线程 ID
{extra}
: 通过 bind
或 patch
添加的额外数据
当你需要为一批日志添加共同的上下文信息时(例如,一个请求的所有日志都包含 request_id
),bind()
方法非常有用。
import sys
from loguru import logger
import uuid
# 默认格式参考 loguru.LOGURU_FORMAT
# "<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
# "<level>{level: <8}</level> | "
# "<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>"
# 配置日志格式,以显示 extra 数据
log_format_with_extra = (
"{time:HH:mm:ss} | {level: <8} | "
"{extra[request_id]} | {name}:{function}:{line} - {message}"
)
# log_format_with_extra = (
# "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | "
# "{name} {function}:{line} - {message} {extra}"
# )
# 当 extra 不存在时,不输出
# 这是一个 trick,通过在格式字符串末尾不加任何额外字符,
# 使得当 extra 字典为空时,不会有任何多余的输出。
# Loguru 会自动处理 extra 的键值对格式化。
# logger.add(sys.stderr, format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message} {extra}")
logger.remove()
logger.add(sys.stdout, format=log_format_with_extra)
# 使用 bind() 创建一个带有预设上下文的 logger
request_id = str(uuid.uuid4())
# bound_logger 会在每一条日志中自动包含 request_id
bound_logger = logger.bind(request_id=request_id)
bound_logger.info("处理用户请求开始...")
# ... 模拟一些操作 ...
bound_logger.debug("正在查询数据库...")
bound_logger.success("请求处理完成。")
# 你也可以在单条日志中通过 extra 参数临时添加数据
bound_logger.patch(lambda record: record["extra"].update(user_ip="192.168.1.100")) \
.info("记录用户IP地址。")
输出示例:
22:45:08 | INFO | 487aee49-3e4b-4c53-bdc3-575e183fc190 | __main__:<module>:20 - 处理用户请求开始...
22:45:08 | DEBUG | 487aee49-3e4b-4c53-bdc3-575e183fc190 | __main__:<module>:22 - 正在查询数据库...
22:45:08 | SUCCESS | 487aee49-3e4b-4c53-bdc3-575e183fc190 | __main__:<module>:23 - 请求处理完成。
22:45:08 | INFO | 487aee49-3e4b-4c53-bdc3-575e183fc190 | __main__:<module>:27 - 记录用户IP地址。
bind()
: 返回一个新的 logger 对象,所有通过它记录的日志都会包含绑定的数据。这对于在不同模块间传递上下文非常有用。
patch()
: 修改 logger 的记录(record),通常用于动态添加一些一次性的上下文信息。
5. 日志级别 (Level)
Loguru 内置了 DEBUG
, INFO
, SUCCESS
, WARNING
, ERROR
, CRITICAL
几个级别。你也可以自定义级别。
import sys
from loguru import logger
# 移除默认配置
logger.remove()
# 配置 Sink,只接收 WARNING 及以上级别的日志
logger.add("logs/errors.log", level="WARNING")
# 另一个 Sink,接收所有 DEBUG 及以上的日志
logger.add("logs/all.log", level="DEBUG")
logger.info("这条日志只会进入 all.log")
logger.warning("这条警告会进入 all.log 和 errors.log")
logger.error("这条错误也会进入两个日志文件")
# ---- 自定义级别 ----
# add() 方法可以设置自定义级别
# level() 方法用于创建新的级别
logger.level("TRACEx", no=5, color="<blue>", icon="🔍")
logger.remove() # 清理之前的配置
logger.add(sys.stdout, level="TRACEx") # 设置最低级别为我们自定义的 TRACEx
logger.trace("这是一条非常详细的追踪信息。")
logger.debug("这是一条调试信息。")
自定义级别输出:
2025-05-02 10:35:00.126 | TRACE | __main__:<module>:26 - 🔍 这是一条非常详细的追踪信息。
2025-05-02 10:35:00.126 | DEBUG | __main__:<module>:27 - 这是一条调试信息。
其他使用
基本使用
from loguru import logger
logger.debug('debug hello world')
logger.debug('this is a debug message')
logger.info('this is another debug message')
logger.warning('this is another debug message')
logger.error('this is another debug message')
logger.info('this is another debug message')
logger.success('this is success message!')
logger.critical('this is critical message!')
2023-05-14 18:27:44.403 | DEBUG | __main__:<module>:1 - debug hello world
...
输出到文件
from loguru import logger
# logger.add("file_{time}.log")
日志轮转/压缩
from loguru import logger
# 每天 12:00 会创建一个新的文件
logger.add("file_{time}.log", rotation="12:00")
# 按周
logger.add('file_{time}.log', rotation='1 week')
# 按大小
logger.add("file_{time}.log", rotation="1 MB", compression="zip", enqueue=True)
logger.debug("That's it, beautiful and simple logging!")
支持 Backtrace
from loguru import logger
# Caution, may leak sensitive data in prod
logger.add("file_{time}.log", backtrace=True, diagnose=True)
def func(a, b):
return a / b
def nested(c):
try:
func(5, c)
except ZeroDivisionError:
logger.exception("What?!")
nested(0)
2023-05-14 18:35:31.069 | ERROR | __main__:nested:5 - What?!
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
> File "<stdin>", line 3, in nested
File "<stdin>", line 2, in func
ZeroDivisionError: division by zero
邮件告警
使用 notifiers 模块当发生 ERROR 级别告警时,发送邮件提醒
import notifiers
from loguru import logger
from notifiers.logging import NotificationHandler
params = {
"username": "from@gmail.com",
"password": "iampassword",
"to": "to@gmail.com"
}
# 初始化时发送一封邮件
notifier = notifiers.get_notifier("gmail")
notifier.notify(message="The application is running!", **params)
# 发生 Error 日志时,发邮件进行警报
handler = NotificationHandler("gmail", defaults=params)
logger.add(handler, level="ERROR")
自定义格式
from loguru import logger
# 配置重定向路径&格式
logger.add('file_{time}.log',format='{level} {time} {message}')
logger.debug('this is a redirect to file message')
from loguru import logger
logger.add("file.log", format="{extra[ip]} {extra[user]} {message}")
context_logger = logger.bind(ip="192.168.0.1", user="someone")
context_logger.info("Contextualize your logger easily")
context_logger.bind(user="someone_else").info("Inline binding of extra attribute")
context_logger.info("Use kwargs to add context during formatting: {user}", user="anybody")
总结
Loguru 通过其简洁的 API 和强大的功能,极大地简化了 Python 中的日志记录工作。其核心优势在于:
- 配置简单: 只需
from loguru import logger
即可开始使用,logger.add()
即可完成复杂的配置。
- 功能全面: 自带日志轮转、压缩、格式化、颜色高亮等高级功能。
- 上下文感知: 通过
bind()
和 patch()
可以轻松实现结构化和上下文相关的日志。
- 异常捕获:
exception()
方法可以自动、完整地记录异常堆栈信息。
无论是简单的脚本调试,还是复杂的大型应用,Loguru 都是一个值得推荐的日志库。