当前位置:首页>python>Python日志革命:Loguru让调试成为一种享受!

Python日志革命:Loguru让调试成为一种享受!

  • 2026-10-11 08:30:48
Python日志革命:Loguru让调试成为一种享受!

 01. 

一、logging让人又爱又恨

说实话,我刚开始用Python的时候,对logging这个模块是又爱又恨。爱它功能强大,恨它配置太繁琐。

记得我第一次在项目里配置日志系统,写了差不多50行代码才搞定基本的文件输出和日志级别控制。你得先创建Logger,再创建Handler,还要配置Formatter,最后把它们拼起来……光是记这些组件的名字就够让人头疼了。

而且最坑的是,有些配置你要么不知道有,要么记不住。比如那个encoding参数,我忘了好几次,导致中文日志乱码。还有那个RotatingFileHandler的maxBytes参数,我一开始设置太小,结果半天就生成了几十个日志文件。

更要命的是异常处理。logging默认不会自动捕获异常,你得自己写try-except,然后在except里调用logger.exception()。我有个同事就因为漏了这一步,在线上出现异常时,日志里只有一个简短的错误信息,根本不知道具体原因是什么,排查了一整晚。

这些都是我踩过的坑。所以当我第一次用Loguru的时候,那种"原来日志还能这么写"的感觉,真的让我眼前一亮。

 02. 

二、零配置,开箱即用

Loguru最打动我的地方,就是它真的做到了"开箱即用"。

你只需要一行导入,就能开始记录日志:

PYTHONfrom loguru import loggerlogger.info("Hello, Loguru!")logger.warning("Something is not right")logger.error("Something went wrong")

就这么简单!不需要任何配置,日志会自动输出到终端,而且带有颜色、时间戳、日志级别等信息,一眼就能看清楚。

我第一次在项目里用Loguru,是因为有个紧急的数据处理任务,需要快速写个脚本。当时我想:反正就是个小脚本,用print调试一下就行了。但写到一半发现,print输出的东西太多了,根本分不清哪些是调试信息,哪些是正常输出。

这时候我想起了之前看到的Loguru,抱着试一试的心态改了一下代码。结果发现,只改了5行代码,就把整个日志系统搭起来了。更神奇的是,不同级别的日志有不同的颜色,在终端里一目了然。

从那以后,我基本上所有的Python项目都用Loguru了。

 03. 

三、一个add()搞定所有输出配置

Loguru的设计哲学很清晰:用最少的代码,完成最多的功能。

最核心的就是logger.add()方法。这个方法可以配置日志输出到任何地方:终端、文件、甚至自定义的函数。

输出到文件

比如你想把日志保存到文件,同时又在终端显示,只需要这样:

PYTHONfrom loguru import logger# 移除默认的处理器logger.remove()# 添加终端输出logger.add(sys.stdout, level="INFO")# 添加文件输出logger.add("app.log", level="DEBUG", rotation="10 MB", retention="7 days")logger.debug("这条只会写入文件")logger.info("这条会同时输出到终端和文件")

这里的rotation参数控制日志轮转,当文件超过10MB时会自动创建新文件。retention参数控制日志保留时间,7天前的日志会被自动删除。

我有个项目刚开始没有配置日志轮转,结果跑了两个月后,日志文件长到了2GB,连编辑器都打不开。后来用了Loguru的rotation参数,这个问题就彻底解决了。

多个文件,不同级别

有时候我们想把不同级别的日志分开保存,比如DEBUG和INFO日志存到debug.log,ERROR和CRITICAL日志存到error.log:

PYTHONlogger.add("debug.log", level="DEBUG", filter=lambda record: record["level"].name in ["DEBUG", "INFO"])logger.add("error.log", level="ERROR", retention="30 days")

这样,调试信息和错误信息就分开了,排查问题时更方便。

 04. 

四、异常追踪,变量值一目了然

Loguru最让我惊喜的功能,是它的异常追踪能力。

传统的logging,即使你用了logger.exception(),也只能看到一个基本的堆栈信息。但Loguru能做到更多:

PYTHONfrom loguru import loggerlogger.add("app.log", backtrace=True, diagnose=True)def divide(a, b):    return a / bdef process_data(data):    result = divide(10, data["value"])    return resultprocess_data({"value": 0})

当这段代码执行时,Loguru会输出一个详细的错误信息,不仅包含完整的堆栈,还显示每个变量的值:

CODETraceback (most recent call last):  File "test.py", line 12, in <module>    process_data({"value": 0})    │           └ {"value": 0}    └ <function process_data at 0x7f8c5c0b3d30>  File "test.py", line 9, in process_data    result = divide(10, data["value"])    │         └ <function divide at 0x7f8c5c0b3ca0>    │                  └ 10    │                  └ 0  File "test.py", line 6, in divide    return a / b           │   └ 0           └ 10ZeroDivisionError: division by zero

看到那个│ └ 0了吗?这就是变量data["value"]的值。你能清楚地看到除数是0,所以才会报除零错误。

我在调试一个复杂的爬虫项目时,这个功能救了我好几次。有一次,一个嵌套了几层的数据处理函数出错了,我光是看错误信息根本不知道是哪个环节出了问题。用Loguru的diagnose模式后,一眼就看到了哪个变量的值不对,5分钟就搞定了原本要排查半小时的问题。

不过要注意,diagnose=True是默认开启的,但这在生产环境可能会泄露敏感信息(比如密码、token)。我就在一次代码审查时被发现,日志里打印了数据库连接密码,还好那是测试环境。从那以后,我在生产环境都会把diagnose设为False。

 05. 

五、装饰器捕获异常,代码更简洁

Loguru还提供了一个超级实用的装饰器:@logger.catch。

PYTHONfrom loguru import loggerlogger.add("app.log")@logger.catchdef main():    logger.info("开始处理数据")    data = read_from_database()    processed = process_data(data)    save_to_file(processed)    logger.success("数据处理完成")if __name__ == "__main__":    main()

这个装饰器会自动捕获main函数里所有未处理的异常,并把完整的错误信息记录到日志。而且它不会阻止异常向上抛出,如果你想让程序继续运行,可以加上reraise=False参数。

我有个CLI工具就是这样写的,不管用户输入什么参数,出错了都能在日志里找到原因。之前用logging的时候,我得在每个可能出错的地方都写try-except,代码变得很臃肿。现在用@logger.catch,一个装饰器搞定所有异常捕获。

 06. 

六、绑定上下文,日志更有业务意义

日志难用的一个原因是,你看到一条错误日志,但不知道是哪个用户、哪个请求产生的。Loguru的bind()方法可以解决这个问题。

PYTHONfrom loguru import loggerlogger.add("app.log", format="{extra[user]} | {extra[request_id]} | {message}")# 创建一个绑定上下文的loggeruser_logger = logger.bind(user="john", request_id="req-123")user_logger.info("开始处理请求")user_logger.error("处理失败")

输出的日志会包含user和request_id信息,方便追踪问题。

如果你有一个完整的请求处理流程,可以用contextualize():

PYTHONfrom loguru import loggerdef handle_request(request):    request_id = request.headers.get("X-Request-ID", "unknown")    with logger.contextualize(request_id=request_id):        logger.info("收到请求")        # ... 处理逻辑 ...        logger.info("处理完成")

这样,在这个with块里的所有日志,都会自动带上request_id。

我做一个Web服务的时候就用了这个方法,给每个HTTP请求分配一个request_id,然后在所有日志里都带上这个ID。当用户反馈问题时,只要提供请求ID,我就能在日志里找到这个请求的完整处理过程,排查效率提高了好几倍。

 07. 

七、结构化日志,方便日志分析

如果你的日志要进ELK、Loki这些日志系统,那结构化日志是必须的。Loguru支持JSON格式输出:

PYTHONfrom loguru import loggerimport sys# 输出JSON格式的日志logger.add(sys.stdout, serialize=True)logger.bind(user="alice", action="login").info("用户登录")logger.bind(user="bob", action="logout").info("用户退出")

输出的日志会是JSON格式,方便日志系统解析:

JSON{"time": "2026-04-15T12:00:00.000Z", "level": "INFO", "message": "用户登录", "extra": {"user": "alice", "action": "login"}}{"time": "2026-04-15T12:00:01.000Z", "level": "INFO", "message": "用户退出", "extra": {"user": "bob", "action": "logout"}}

我在做数据分析任务时,经常用这个功能。因为日志是结构化的,可以直接用pandas读取,做统计分析。

 08. 

八、异步日志,不阻塞主线程

如果你的应用需要高性能,那日志不能成为瓶颈。Loguru支持异步日志:

PYTHONfrom loguru import logger# enqueue=True会让日志写入变成异步的logger.add("app.log", enqueue=True, rotation="100 MB")for i in range(100000):    logger.info(f"Processing item {i}")

加了enqueue=True后,日志写入会被放到一个队列里,由单独的线程处理,不会阻塞主线程。我试过在循环里写10万条日志,用异步日志比同步日志快了将近10倍。

 09. 

九、与FastAPI/Django集成

很多人问我,Loguru能不能和Web框架配合使用?答案是:不仅能,而且配合得很好。

FastAPI集成

PYTHONfrom fastapi import FastAPIfrom loguru import loggerimport sysapp = FastAPI()# 移除uvicorn的默认日志logger.remove()# 添加自定义日志配置logger.add(sys.stdout, level="INFO", format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}")logger.add("api.log", rotation="50 MB", retention="30 days", level="DEBUG")@app.middleware("http")async def log_requests(request, call_next):    logger.info(f"{request.method} {request.url}")    response = await call_next(request)    logger.info(f"Status: {response.status_code}")    return response@app.get("/")async def root():    logger.debug("Debug message")    logger.info("Info message")    return {"message": "Hello"}

Django集成

PYTHON# settings.pyfrom loguru import loggerimport sys# 移除Django的默认日志LOGGING_CONFIG = None# 配置Logurulogger.remove()logger.add(sys.stdout, format="{time} | {level} | {message}", level="INFO")logger.add("django.log", rotation="50 MB", retention="30 days", level="DEBUG")# 创建一个自定义的logging handlerclass LoguruHandler(logging.Handler):    def emit(self, record):        logger.opt(exception=record.exc_info).log(            record.levelno, record.getMessage()        )# 配置Django使用Loguruimport logginglogging.root.addHandler(LoguruHandler())

我有个Django项目就是这么配置的,用了快一年了,没出过任何问题。而且Loguru的日志格式比Django默认的好看多了。

 10. 

十、从logging迁移到Loguru

如果你已经在用logging了,迁移到Loguru其实很简单。Loguru官方提供了详细的迁移指南,我总结几个最常用的替换:

1. 获取logger:logging.getLogger(__name__) → from loguru import logger

2. 添加handler:logger.addHandler(handler) → logger.add(sink, ...)

3. 设置格式:formatter = logging.Formatter(...) → logger.add(..., format=...)

4. 异常日志:logger.exception(msg) → logger.exception(msg)(这个保持不变)

5. 日志级别:logger.setLevel(logging.INFO) → logger.add(..., level="INFO")

我试过把一个10万行的项目从logging迁移到Loguru,只用了半天时间。代码量减少了30%,日志功能反而更强大了。

 11. 

十一、什么时候不用Loguru

当然,Logurus也不是万能的。如果你的项目有这些情况,可能还是继续用logging比较好:

1. 严格的依赖限制:有些公司不允许引入第三方库

2. 已经有成熟的日志基建:比如有一套基于logging的封装,团队都习惯了

3. 需要极高的自定义:logging的扩展性确实比Loguru强

我之前参与过一个政府项目,就因为安全审查不允许引入第三方库,所以只能用logging。这种情况确实没办法,但对大多数项目来说,Loguru是更好的选择。

 12. 

十二、总结

用了Loguru快两年了,我觉得它最大的价值不是"让日志变好看了",而是把日志从一堆胶水配置,重新拉回到更像工程能力的状态。

如果你经常写CLI工具、批处理任务、API服务、AI工作流,Loguru真的值得认真学一下。它不会让你写出更多的代码,反而会让你写出更少的代码,但功能更强大。

我现在的原则是:新项目一律用Loguru,老项目有机会就迁移。相信我,当你第一次看到Loguru输出的彩色日志,看到异常追踪时显示的变量值,你也会觉得:原来日志还可以这么爽!

关注我,每天一个Python模块,让你的Python代码更优雅!

最新文章

随机文章