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
pip install loguruLoguru 核心用法与示例
下面我们将通过具体的代码示例来展示 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 zero2. 添加与配置 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: 设置旧日志文件的压缩格式。
3. 自定义日志格式 (Format)
通过 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添加的额外数据
4. 使用 bind 和 extra 添加上下文信息
当你需要为一批日志添加共同的上下文信息时(例如,一个请求的所有日志都包含 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')
extra
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 都是一个值得推荐的日志库。