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代码更优雅!