Loguru:让 Python 日志记录变得极其简单

你有没有过这种感觉:配置 logger 太麻烦了,干脆直接用 print() 吧?

如果你有过这种想法,你并不孤单。虽然日志记录对每个应用程序都至关重要,能极大地简化调试过程,但 Python 标准库的 logging 模块配置复杂、代码冗长,让开发者望而却步。

Loguru 正是为了解决这个问题而生的——它的口号是 “Python logging made (stupidly) simple”

本文将深入介绍 Loguru 这个让日志记录变得愉悦的 Python 库,展示它如何用一行代码替代数十行标准 logging 配置。

前言

你有没有过这种感觉:配置 logger 太麻烦了,干脆直接用 print() 吧?

如果你有过这种想法,你并不孤单。虽然日志记录对每个应用程序都至关重要,能极大地简化调试过程,但 Python 标准库的 logging 模块配置复杂、代码冗长,让开发者望而却步。

Loguru 正是为了解决这个问题而生的——它的口号是 “Python logging made (stupidly) simple”

本文将深入介绍 Loguru 这个让日志记录变得愉悦的 Python 库,展示它如何用一行代码替代数十行标准 logging 配置。


什么是 Loguru?

核心理念

Loguru 是一个让 Python 日志记录变得简单愉悦的库。它的设计目标是:

  • 开箱即用:无需繁琐配置,导入即用
  • 统一接口:一个函数 add() 处理所有配置
  • 强大功能:解决标准 logging 的各种痛点
  • 自然习惯:让日志记录成为开发者的自动行为

项目信息

属性 信息
作者 Delgan
PyPI loguru
GitHub Delgan/loguru
文档 Read the Docs
许可证 MIT
性能 比内置 logging 快 10 倍

安装

使用 pip 安装非常简单:

1
pip install loguru

快速开始:一行代码替代数十行配置

标准 logging vs Loguru

对比一下两种方式的复杂度:

标准 logging(Python 内置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import logging

# 创建 logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# 创建 handler
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)

# 创建 formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)

# 添加 handler
logger.addHandler(handler)

# 记录日志
logger.debug("That's it, beautiful and simple logging!")

Loguru

1
2
3
from loguru import logger

logger.debug("That's it, beautiful and simple logging!")

输出

1
2026-02-14 10:50:00 | DEBUG    | That's it, beautiful and simple logging!

🎯 Loguru 默认配置

  • 输出到 stderr
  • 带颜色的日志级别
  • 包含时间戳
  • 清晰的格式

核心特性

1. 开箱即用,无需样板代码

Loguru 预配置了一个全局 logger,可以直接使用:

1
2
3
4
5
from loguru import logger

logger.info("Hello, Loguru!")
logger.warning("This is a warning")
logger.error("Something went wrong!")

输出(带颜色):

1
2
3
2026-02-14 10:50:00 | INFO     | Hello, Loguru!
2026-02-14 10:50:00 | WARNING | This is a warning
2026-02-14 10:50:00 | ERROR | Something went wrong!

2. 一个函数统治所有:add()

Loguru 用一个 add() 函数替代了标准 logging 的多个概念:

  • Handler → Sink
  • Formatterformat 参数
  • Filterfilter 参数
  • Levellevel 参数
1
2
3
4
5
6
7
8
import sys

logger.add(
sys.stderr,
format="{time} {level} {message}",
filter="my_module",
level="INFO"
)

重要add() 会返回一个标识符,可以用 logger.remove(identifier) 移除 handler。

移除默认 handler 并重新开始:

1
logger.remove()  # 移除所有默认 handlers

3. 轻松的文件日志管理

这是 Loguru 最强大的功能之一,支持自动轮转、保留和压缩

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 自动按时间命名
logger.add("file_{time}.log")

# 文件超过 500MB 时自动轮转
logger.add("file_1.log", rotation="500 MB")

# 每天中午 12:00 创建新文件
logger.add("file_2.log", rotation="12:00")

# 每周轮转一次
logger.add("file_3.log", rotation="1 week")

# 自动清理 10 天前的日志
logger.add("file_X.log", retention="10 days")

# 关闭时压缩为 zip
logger.add("file_Y.log", compression="zip")

组合使用

1
2
3
4
5
6
7
logger.add(
"app.log",
rotation="1 day", # 每天轮转
retention="30 days", # 保留 30 天
compression="zip", # 压缩旧日志
encoding="utf-8" # UTF-8 编码
)

4. 现代字符串格式化

Loguru 使用 Python 的 {} 格式化,而不是老式的 % 格式:

1
2
3
4
5
6
7
logger.info(
"If you're using Python {}, prefer {feature} of course!",
3.6,
feature="f-strings"
)

# 输出: If you're using Python 3.6, prefer f-strings of course!

5. 异常捕获:catch() 装饰器

问题:程序崩溃时看不到日志?线程中的异常没有被记录?

解决方案:使用 @logger.catch 装饰器:

1
2
3
4
5
6
@logger.catch
def my_function(x, y, z):
return 1 / (x + y + z)

# 调用
my_function(1, 2, -3) # 会抛出 ZeroDivisionError

输出(带完整堆栈跟踪):

1
2
3
4
5
2026-02-14 10:50:00 | ERROR    | An error has been caught in function 'my_function'
Traceback (most recent call last):
File "test.py", line 3, in my_function
return 1 / (x + y + z)
ZeroDivisionError: division by zero

6. 彩色日志输出

Loguru 自动为终端输出添加颜色,支持自定义样式:

1
2
3
4
5
6
7
import sys

logger.add(
sys.stdout,
colorize=True,
format="<green>{time}</green> <level>{message}</level>"
)

可用的颜色标签

1
2
3
<red>, <blue>, <green>, <yellow>, <cyan>, <magenta>
<b>, <i>, <u> (粗体、斜体、下划线)
<lvl>, <d> (日志级别、深度)

7. 异步、线程安全、进程安全

线程安全:所有 sinks 默认线程安全

异步日志:使用 enqueue=True 确保日志完整性

1
logger.add("somefile.log", enqueue=True)

异步 sinks:支持协程函数

1
2
3
4
5
async def async_sink(message):
# 处理日志消息
pass

logger.add(async_sink)

8. 完整的异常描述

问题:记录了异常,但不知道为什么会失败?

解决方案backtrace=Truediagnose=True

1
2
3
4
5
6
7
8
9
10
11
12
logger.add("out.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)

输出(包含变量值):

1
2
3
4
5
6
7
8
9
10
nested(0)
└> File "test.py", line 8, in nested
func(5, c)
│ └ 0
└> File "test.py", line 4, in func
return a / b
│ └ 0
└ 5

ZeroDivisionError: division by zero

⚠️ 安全提示diagnose=True 可能在生产环境中泄露敏感数据,请谨慎使用。

9. 结构化日志

使用 serialize=True 将日志转换为 JSON:

1
2
3
4
5
6
7
import json

def json_sink(message):
# message 已经是 JSON 字符串
print(message)

logger.add(json_sink, serialize=True)

输出示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"record": {
"elapsed": {...},
"exception": null,
"extra": {},
"file": {...},
"function": "test",
"level": {"name": "INFO", "no": 20},
"line": 42,
"message": "Hello, world!",
"module": "__main__",
"name": "__main__",
"process": {...},
"thread": {...},
"time": {...}
},
"text": "2026-02-14 10:50:00 | INFO | Hello, world!\n"
}

上下文绑定bind() 方法添加额外上下文

1
2
3
4
5
6
7
8
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")

context_logger.info("Use kwargs: {user}", user="anybody")

上下文管理器contextualize() 方法

1
2
3
with logger.contextualize(task=task_id):
do_something()
logger.info("End of task")

10. 懒惰求值

避免在生产环境中执行昂贵的日志操作:

1
2
3
4
5
6
7
8
def expensive_function(n):
return sum(i ** 2 for i in range(n))

# 只在 DEBUG 级别时执行
logger.opt(lazy=True).debug(
"If sink level <= DEBUG: {x}",
x=lambda: expensive_function(2**64)
)

opt() 的其他用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 添加异常信息
logger.opt(exception=True).info("Error info added")

# 强制使用颜色
logger.opt(colors=True).info("Per message <blue>colors</blue>")

# 显示 record 信息
logger.opt(record=True).info("Thread: {record[thread]}")

# 原始输出(不格式化)
logger.opt(raw=True).info("Bypass sink formatting\n")

# 使用父栈上下文
logger.opt(depth=1).info("Use parent stack context")

# 不添加到 extra 字典
logger.opt(capture=False).info("No kwargs in {dest}", dest="extra")

11. 自定义日志级别

Loguru 添加了 trace()success() 级别,还可以自定义:

1
2
3
4
5
6
7
8
9
10
# 添加新级别
new_level = logger.level(
"SNAKY",
no=38,
color="<yellow>",
icon="🐍"
)

# 使用新级别
logger.log("SNAKY", "Here we go!")

标准级别

  • TRACE(5):最详细的信息
  • DEBUG(10):调试信息
  • INFO(20):常规信息
  • SUCCESS(25):成功信息(Loguru 特有)
  • WARNING(30):警告信息
  • ERROR(40):错误信息
  • CRITICAL(50):严重错误

12. 更好的时间处理

标准 logging 的时间处理复杂,Loguru 简化了:

1
2
3
4
logger.add(
"file.log",
format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}"
)

格式说明

  • {time}:默认格式
  • {time:YYYY-MM-DD}:自定义格式
  • {time:YYYY-MM-DD HH:mm:ss}:详细时间
  • 完整支持 Python 的 strftime 格式

13. 脚本和库的适配

对于脚本

1
2
3
4
5
6
7
8
config = {
"handlers": [
{"sink": sys.stdout, "format": "{time} - {message}"},
{"sink": "file.log", "serialize": True},
],
"extra": {"user": "someone"}
}
logger.configure(**config)

对于库

1
2
3
4
5
6
7
# 在你的库中
logger.disable("my_library")
logger.info("This message is not displayed")

# 在使用你库的应用中
logger.enable("my_library")
logger.info("This message is now displayed")

最佳实践

  • 库不应该调用 add()
  • 库应该默认禁用日志
  • 让使用者决定是否启用

14. 与标准 logging 完全兼容

使用标准 logging Handler

1
2
3
4
import logging

handler = logging.handlers.SysLogHandler(address=('localhost', 514))
logger.add(handler)

将 Loguru 消息传播到标准 logging

1
2
3
4
5
class PropagateHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
logging.getLogger(record.name).handle(record)

logger.add(PropagateHandler(), format="{message}")

拦截标准 logging 到 Loguru

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import inspect

class InterceptHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno

frame, depth = inspect.currentframe(), 0
while frame:
filename = frame.f_code.co_filename
is_logging = filename == logging.__file__
is_frozen = "importlib" in filename and "_bootstrap" in filename
if depth > 0 and not (is_logging or is_frozen):
break
frame = frame.f_back
depth += 1

logger.opt(depth=depth, exception=record.exc_info).log(
level, record.getMessage()
)

logging.basicConfig(handlers=[InterceptHandler()], level=0, force=True)

15. 环境变量配置

通过环境变量自定义默认行为:

1
2
3
4
5
6
7
# Linux / macOS
export LOGURU_FORMAT="{time} | <lvl>{message}</lvl>"
export LOGURU_DEBUG_COLOR="<green>"

# Windows
setx LOGURU_FORMAT "{time} | {message}"
setx LOGURU_DEBUG_COLOR "<green>"

支持的变量

  • LOGURU_FORMAT:默认格式
  • LOGURU_LEVEL:默认日志级别
  • LOGURU_DEBUG_COLOR:DEBUG 级别颜色
  • NO_COLOR:禁用颜色
  • FORCE_COLOR:强制使用颜色

16. 日志解析器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import dateutil.parser

# 定义正则模式
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)"

# 转换函数
caster_dict = {
"time": dateutil.parser.parse,
"level": int
}

# 解析日志文件
for groups in logger.parse("file.log", pattern, cast=caster_dict):
print("Parsed:", groups)

17. 通知集成

结合 apprise 库发送通知:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import apprise

WEBHOOK_ID = "123456790"
WEBHOOK_TOKEN = "abc123def456"

notifier = apprise.Apprise()
notifier.add(f"discord://{WEBHOOK_ID}/{WEBHOOK_TOKEN}")

# 在错误时发送通知
logger.add(
notifier.notify,
level="ERROR",
filter=lambda record: "apprise" not in record["name"]
)

18. 性能优势

Loguru 比内置 logging 快 10 倍

在大多数情况下,logging 对性能的影响可以忽略不计,但一个零成本的日志记录器允许在任何地方使用它而无需太多顾虑。

注意:未来的 Loguru 版本中,关键函数将用 C 语言实现以获得最大速度。


实际应用场景

场景 1:Web 应用日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from loguru import logger

# 配置文件日志
logger.add(
"app.log",
rotation="1 day",
retention="30 days",
compression="zip",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {name}:{function}:{line} | {message}"
)

# 错误单独记录
logger.add(
"errors.log",
level="ERROR",
format="{time:YYYY-MM-DD HH:mm:ss} | {exception}"
)

# JSON 格式用于日志分析系统
logger.add(
"logs.json",
serialize=True,
rotation="100 MB",
compression="gzip"
)

@logger.catch
def process_request(request):
logger.info(f"Processing request: {request.url}")
# 处理逻辑
logger.success("Request processed successfully")

场景 2:数据科学项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from loguru import logger

# 训练过程日志
logger.add(
"training.log",
format="{time} | {level} | {message}",
rotation="500 MB"
)

def train_model():
logger.info("Starting training...")

for epoch in range(100):
# 懒惰求值:只在 DEBUG 时计算
logger.opt(lazy=True).debug(
"Epoch {epoch} stats: {stats}",
epoch=epoch,
stats=lambda: compute_expensive_stats()
)

# 训练逻辑

logger.success("Training completed!")

场景 3:异步任务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from loguru import logger
import asyncio

# 异步日志
logger.add(
"async_tasks.log",
enqueue=True, # 确保线程安全
format="{time} | {level} | {message}"
)

@logger.catch
async def fetch_data(url):
logger.info(f"Fetching: {url}")
data = await fetch(url)
logger.success(f"Data fetched: {len(data)} bytes")
return data

async def main():
tasks = [fetch_data(url) for url in urls]
await asyncio.gather(*tasks)

场景 4:微服务架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from loguru import logger

# 每个服务自己的日志
logger.add(
f"service_{os.getenv('SERVICE_NAME')}.log",
format="{time} | {level} | {extra[service]} | {message}",
rotation="100 MB",
compression="zip"
)

service_logger = logger.bind(service=os.getenv('SERVICE_NAME'))

@logger.catch
def handle_request(request):
service_logger.info(f"Request: {request.method} {request.path}")
try:
# 处理请求
service_logger.success("Request completed")
except Exception as e:
service_logger.error(f"Error: {e}")
raise

最佳实践

1. 日志级别选择

级别 用途 何时使用
TRACE 最详细信息 开发调试,跟踪执行流程
DEBUG 调试信息 开发和测试,诊断问题
INFO 常规信息 生产环境,关键业务流程
SUCCESS 成功信息 关键操作成功,用户反馈
WARNING 警告信息 潜在问题,需要关注
ERROR 错误信息 错误发生,但程序继续运行
CRITICAL 严重错误 严重错误,可能导致程序崩溃

2. 格式化建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 开发环境
dev_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>"
)

# 生产环境
prod_format = (
"{time:YYYY-MM-DD HH:mm:ss} | "
"{level} | "
"{message}"
)

logger.add(sys.stdout, format=dev_format, level="DEBUG")
logger.add("prod.log", format=prod_format, level="INFO")

3. 错误处理

1
2
3
4
5
6
7
8
# 捕获未处理的异常
@logger.catch(onerror=lambda e: logger.critical(f"Unhandled: {e}"))
def main():
# 你的代码
pass

if __name__ == "__main__":
main()

4. 性能优化

1
2
3
4
5
6
7
8
9
10
11
12
# 使用懒惰求值
logger.opt(lazy=True).debug(
"Expensive data: {data}",
data=lambda: compute_expensive_data()
)

# 异步日志(高并发场景)
logger.add("app.log", enqueue=True)

# 条件日志
if logger.level("DEBUG").no <= 20:
logger.debug("This is expensive!")

5. 安全考虑

⚠️ 生产环境注意事项

1
2
3
4
5
6
7
8
9
# 不在生产环境启用 diagnose
if os.getenv("ENV") == "production":
logger.add("app.log", backtrace=True, diagnose=False)
else:
logger.add("app.log", backtrace=True, diagnose=True)

# 不记录敏感信息
logger.info("User logged in") # ✅
# logger.info(f"User {username} logged in with {password}") # ❌

与标准 logging 对比

特性 标准 logging Loguru
配置复杂度 高(Handler + Formatter + Filter) 低(一个 add() 函数)
文件轮转 需要手动实现 内置支持(rotation, retention, compression)
字符串格式化 %s 风格 {} 风格(Python 标准)
异常捕获 需要手动 try-catch @logger.catch 装饰器
彩色输出 需要额外配置 默认支持
性能 基准 快 10 倍
结构化日志 需要自定义格式 serialize=True
代码行数 ~10 行 ~1 行

常见问题

Q1: Loguru 会替换标准 logging 吗?

不会,但可以无缝集成。你可以:

  • 使用标准 logging Handler 作为 Loguru sink
  • 拦截标准 logging 到 Loguru
  • 同时使用两者

Q2: Loguru 适合生产环境吗?

适合,但需要注意:

  • 使用适当的日志级别
  • 禁用 diagnose=True(避免泄露敏感数据)
  • 配置合适的轮转和保留策略
  • 使用 enqueue=True 确保线程安全

Q3: 如何在库中使用 Loguru?

最佳实践

1
2
3
4
5
6
7
8
9
10
11
12
# 你的库代码中
from loguru import logger
logger.disable(__name__) # 默认禁用

def your_function():
logger.info("This won't be shown by default")
# 你的代码

# 用户代码中
from your_library import your_function
logger.enable("your_library") # 按需启用
your_function() # 现在会显示日志

Q4: Loguru 支持哪些 sinks?

支持的 sink 类型

  • 文件路径(字符串)
  • 文件对象(类文件)
  • 标准流(sys.stdout, sys.stderr
  • 函数
  • 协程函数
  • 标准 logging Handler
  • 自定义 sink 类

总结

Loguru 通过简化 API 和提供强大功能,让 Python 日志记录变得简单、愉悦且强大

核心优势

  1. 极其简单:一行代码开始使用
  2. 功能强大:内置轮转、压缩、异步等
  3. 性能优秀:比标准 logging 快 10 倍
  4. 开发友好:彩色输出、异常捕获、懒求值
  5. 生产就绪:结构化日志、线程安全、可配置

适用场景

  • ✅ Web 应用开发
  • ✅ 数据科学项目
  • ✅ 异步任务
  • ✅ 微服务架构
  • ✅ 脚本和工具
  • ✅ 库开发(按需启用)

资源链接


如果你还在使用 print() 或被标准 logging 的复杂配置困扰,试试 Loguru 吧——它会让你重新爱上日志记录!