Loguru: Python 日志使用介绍

发布时间: 更新时间: 总字数:3405 阅读时间:7m 作者: IP上海 分享 网址

Loguru 以其开箱即用的特性和简洁的 API 设计,在 Python 开发者中广受欢迎。相比于 Python 内置的 logging 模块,Loguru 在很多场景下能让日志记录变得更加简单和直观

使用场景

  1. 替代 print() 进行调试: 在开发过程中,我们经常使用 print() 输出变量或程序状态。但这种方式在代码发布后通常需要手动移除,且无法控制输出级别。Loguru 提供了更灵活的方式,可以轻松切换日志级别,在生产环境中自动关闭调试信息,同时保留错误记录。
  2. Web 开发后端日志: 在如 Flask, Django, FastAPI 等 Web 框架中,记录请求信息、数据库查询、业务逻辑处理流程和异常捕获至关重要。Loguru 可以轻松地将日志输出到文件,并按日期、大小自动分割(Rotating Files),方便排查线上问题。
  3. 数据科学与机器学习: 在长时间运行的数据处理或模型训练任务中,记录关键步骤、中间结果、性能指标和可能出现的错误非常有用。通过 Loguru,可以清晰地追踪任务执行进度,方便复现和分析。
  4. 命令行工具 (CLI): 为命令行工具添加日志功能,可以向用户展示程序执行的详细过程,或者将错误信息记录到文件中供开发者分析。Loguru 可以同时向控制台和文件输出不同级别的日志,非常方便。
  5. 异步编程 (Asyncio): Loguru 对异步代码有良好的支持,可以安全地在 async/await 代码中记录日志,而不会引发线程安全问题。
  6. 结构化日志 (Structured Logging): Loguru 可以方便地输出 JSON 格式的日志。这对于将日志发送到集中式日志管理平台(如 ELK Stack, Splunk, Datadog)进行分析、监控和告警非常关键。

安装 Loguru

pip install 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: 设置旧日志文件的压缩格式。

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}: 通过 bindpatch 添加的额外数据

4. 使用 bindextra 添加上下文信息

当你需要为一批日志添加共同的上下文信息时(例如,一个请求的所有日志都包含 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 都是一个值得推荐的日志库。

本文总阅读量 次 本站总访问量 次 本站总访客数
Home Archives Categories Tags Statistics