loguru 是 python 第三方库,用于替代 logging 库,不需要手动创建 logger,开箱即用;
另外,日志输出内置了彩色功能,颜色和非颜色控制很方便,更加友好。
参考:loguru 官方文档
# 概述
在 Python 中用到日志记录,那就不可避免地会用到内置的 logging
标准库 。虽然 logging
库采用的是模块化设计,你可以设置不同的 handler
来进行组合,但是在配置上通常较为繁琐;而且如果不是特别处理,在一些多线程或多进程的场景下使用 logging
还会导致日志记录会出现错乱或是丢失的情况。
loguru
是标准库 logging
的替代品,它不仅能够减少繁琐的配置过程还能实现和 logging
类似的功能,同时还能保证日志记录的线程进程安全,又能够和 logging
相兼容,并进一步追踪异常也能进行代码回溯。
# 基本使用
logger
本身就是一个已经实例化好的对象,如果没有特殊的配置需求,那么自身就已经带有通用的配置参数,同时它的用法和 logging
库输出日志时的用法一致。
# 安装
loguru
是非标准库,需要事先安装: pip install loguru
# 日志输出
loguru
默认的输出格式是:时间、级别、模块、行号以及日志内容。
# 输出到控制台
安装后,最简单的使用样例如下:
from loguru import logger | |
logger.debug ('hello, this debug loguru') | |
logger.info ('hello, this is info loguru') | |
logger.warning ('hello, this is warning loguru') | |
logger.error ('hello, this is error loguru') | |
logger.critical ('hello, this is critical loguru') |
上述代码输出如下:
2025-09-22 22:53:31.174 | DEBUG | __main__:<module>:5 - hello, this debug loguru | |
2025-09-22 22:53:31.175 | INFO | __main__:<module>:6 - hello, this is info loguru | |
2025-09-22 22:53:31.175 | WARNING | __main__:<module>:7 - hello, this is warning loguru | |
2025-09-22 22:53:31.175 | ERROR | __main__:<module>:8 - hello, this is error loguru | |
2025-09-22 22:53:31.175 | CRITICAL | __main__:<module>:9 - hello, this is critical loguru |
# 输出到文件
日志打印到文件的用法也很简单,代码如下,运行时可以将日志打印到 console,也可以打印到文件中去。
from loguru import logger | |
logger.add ('myloguru.log') | |
logger.debug ('hello, this debug loguru') | |
logger.info ('hello, this is info loguru') | |
logger.warning ('hello, this is warning loguru') | |
logger.error ('hello, this is error loguru') | |
logger.critical ('hello, this is critical loguru') |
# 处理器参数
在 Loguru
中,可以使用 logger.configure ()
方法来设置日志处理器(handlers)。 handlers
是一个列表,定义了日志的输出处理器。定义内容常为字典形式。格式样例:
from loguru import logger | |
logger.configure (handlers=[ | |
{ | |
"sink": sys.stderr, | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>mymodule</> | - <lvl>{message}</>", | |
"colorize": True | |
}, | |
]) |
handlers
的常见参数如下
参数 | 含义 | 示例 |
---|---|---|
sink | 指定日志输出的目标,即标准错误流(控制台)或者是文件中。 | {"sink": sys.stderr} 输出到标准错误流 <br /> {"sink":"app.log"} 输出到文件 |
encoding | 指定文件输出的编码,仅当 sink 是文件时有效。 | {"encoding":"utf-8"} 指定文件编码方式为 "utf-8" |
colorize | 设置为 True 时,启用终端颜色输出,使日志更易读(级别和固定字符串会根据颜色标记显示颜色)。 | {"encoding": True} 启用终端颜色输出 |
filter | 类型为函数或字符串,用于过滤日志记录。 | {"filter":"my_module"} 只记录来自指定模块 "my_module" 的日志。{"filter": lambda record: record ["level"].name =="ERROR"} 只记录 ERROR 级别的日志。 |
level | 设置此处理器的最低日志级别。低于此级别的日志不会被此处理器处理。默认情况下,所有级别都会被处理。 | {"level":"WARNING"} 不记录 WARNING 以下级别的日志 |
serialize | 布尔值。设置为 True 时,日志记录会序列化为 JSON 格式,便于结构化存储或传输。默认 False。 | {"serialize": True} 将日志序列化为 JSON 格式 |
enqueue | 布尔值。设置为 True 时,日志记录会异步处理(放入队列),避免阻塞主线程。默认 False。适用于高频日志场景 | {"enqueue": True} 异步处理日志 |
catch | 布尔值。设置为 True 时,处理器会捕获并记录自己的异常(例如,写入文件失败)。默认 False。 | {"catch": True} 捕获并记录 loguru 自身的异常 |
rotation | 定义日志文件的轮转策略。 | {"rotation":"1 day"} 每天轮转。{"rotation":"10 MB"} 文件大小超过 10MB 时轮转。{"rotation":"00:00"} 每天午夜轮转。 |
retention | 定义日志文件的保留策略。 | 例如: "7 days":保留 7 天的日志。 "5 files":保留最新的 5 个文件。 |
compression | 定义日志文件的压缩格式。 | {"compression":"zip"} 压缩为 ZIP。{"compression":"gz"} :压缩为 GZIP。 |
backtrace | 布尔值。设置为 True 时,在异常日志中显示完整的回溯信息(包括调用栈)。默认 False。 | {"backtrace": True} 在异常日志中显示完整的回溯信息 |
diagnose | 布尔值。设置为 True 时,在异常日志中显示更详细的诊断信息(如变量值)。默认 False。注意:这会影响性能。 | {"diagnose": True} 在异常日志中显示更详细的诊断信息 |
# 进阶用法
# 显示格式
loguru
默认格式是 时间 | 级别 | 名称:模块:日志内容
,其中 名称:模块
是写死的,是当前文件的 __name__
变量,此变量最好不要修改。
工程比较复杂的情况下,自定义模块名称更容易定界定位,避免陷入细节中。可以通过 logger.configure
手工指定模块名称,方法为:
logger.configure
相关参数
handlers
:表示日志输出句柄或者目的地sink
:sys.stderr
表示输出到终端format
: 表示日志格式化。{level:8}</> 表示按照日志级别显示颜色。8 表示输出宽度为 8 个字符colorize
:True
表示显示颜色
import sys | |
from loguru import logger | |
logger.configure (handlers=[ | |
{ | |
"sink": sys.stderr, | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>mymodule</> | - <lvl>{message}</>", | |
"colorize": True | |
}, | |
]) | |
logger.debug ('this is debug') | |
logger.info ('this is info') | |
logger.warning ('this is warning') | |
logger.error ('this is error') | |
logger.critical ('this is critical') |
# 写入文件
日志一般需要持久化,除了输出到命令行终端外,还需要写入文件。通过在 logger.configure
新增了 handler
,写入到日志文件中去。示例如下:
import sys | |
from loguru import logger | |
logger.configure (handlers=[ | |
{ | |
"sink": sys.stderr, | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>mymodule</> | - <lvl>{message}</>", | |
"colorize": True | |
}, | |
{ | |
"sink": 'first.log', | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |{level:8}| {name} : {module}:{line:4} | mymodule | - {message}", | |
"colorize": False | |
}, | |
]) | |
logger.debug ('this is debug') | |
logger.info ('this is info') | |
logger.warning ('this is warning') | |
logger.error ('this is error') | |
logger.critical ('this is critical') |
# 模块名参数化
实际项目开发中,不同模块写日志,需要指定不同的模块名称。因此,模块名称需要参数化,这样实用性更强。
logger.bind (module_name=‘my-loguru’)
通过 bind
方法,实现 module_name
的参数化。 bind
返回一个日志对象,可以通过此对象进行日志输出,这样就可以实现不同模块的日志格式。
import sys | |
from loguru import logger | |
logger.configure (handlers=[ | |
{ | |
"sink": sys.stderr, | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra [module_name]}</> | - <lvl>{message}</>", | |
"colorize": True | |
}, | |
{ | |
"sink": 'first.log', | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |{level:8}| {name} : {module}:{line:4} | {extra [module_name]} | - {message}", | |
"colorize": False | |
}, | |
]) | |
log = logger.bind (module_name='my-loguru') | |
log.debug ("this is hello, module is my-loguru") | |
log2 = logger.bind (module_name='my-loguru2') | |
log2.info ("this is hello, module is my-loguru2") |
# 日志留存、压缩与清理
通常来说如果程序或服务的量级较大,那么就可以通过集成的日志平台或数据库来对日志信息进行存储和留存,后续有需要的话也方便进行日志分析。
可以通过对 rotation
、 compression
和 retention
三个参数进行设定来满足我们的需要,
rotation
:将日志记录以文件大小、时间等方式进行分割或划分。
# 设置每天 0 点新创建一个 log 文件: | |
logger.add ('runtime_{time}.log', rotation='00:00') | |
# 设置超过 500 MB 新创建一个 log 文件: | |
logger.add ('runtime_{time}.log', rotation="500 MB") | |
# 设置每隔一个周新创建一个 log 文件: | |
logger.add ('runtime_{time}.log', rotation='1 week') |
compression
:对日志进行压缩后留存。
# 设置使用 zip 文件格式保存 | |
logger.add ('runtime_{time}.log', compression='zip') | |
# 设置使用 gz 文件格式保存 | |
logger.add ('runtime_{time}.log', compression='gz') |
retention
:只保留一段时间内的日志并对超期的日志进行删除。
from loguru import logger | |
import datetime | |
# 设置日志文件最长保留 15 天: | |
logger.add ('runtime_{time}.log', retention='15 days') | |
# 设置日志文件最多保留 10 个: | |
logger.add ('runtime_{time}.log', retention=10) | |
# retention 的参数也可以是一个 datetime.timedelta 对象,比如设置日志文件最多保留 5 个小时: | |
logger.add ('runtime_{time}.log', retention=datetime.timedelta (hours=5)) |
# 序列化为 json 格式
通过 serialize
参数可以日志转化成序列化的 json 格式,最后将导入类似于 MongoDB、ElasticSearch 这类数 NoSQL 数据库中用作后续的日志分析。
loguru
保存成结构化 json
格式非常简单,只需要设置 serialize=True
参数即可。代码如下
from loguru import logger | |
logger.add ('json.log', serialize=True, encoding='utf-8') | |
logger.debug ('this is debug message') | |
logger.info ('this is info message') | |
logger.error ('this is error message') |
# 并发安全
loguru
默认是线程安全的,但不是多进程安全的,如果使用了多进程安全,需要添加参数 enqueue=True,样例代码如下:
logger.add ("somefile.log", enqueue=True) |
# 使用示例
# 多模块调用
设置一个基础的日志模块,所有其他模块统一调用基础模块记录日志
# -*- coding: utf-8 -*- | |
# loguru_base.py | |
# 基础日志模块,提供日志配置和获取 loguru 实例的功能 | |
import sys | |
from loguru import logger | |
logger.configure (handlers=[ | |
{ | |
"sink": sys.stderr, | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra [module]}</> | - <lvl>{message}</>", | |
"colorize": True | |
}, | |
{ | |
"sink": "app.log", | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra [module]}</> | - <lvl>{message}</>", | |
"colorize": True | |
} | |
]) | |
def get_logger (module_name: str = "default"): | |
return logger.bind (module=module_name) | |
# 导出 get_logger 函数供其他模块使用 | |
__all__ = ["get_logger"] |
在其余模块中调用:
from loguru_base import get_logger | |
logger = get_logger ("test_module") | |
logger.info ("这是一个信息日志") | |
logger.error ("这是一个错误日志") | |
logger.debug ("这是一个调试日志") |
# 在 Qt 中应用
我们希望将日志信息输出到一个 QTextEdit
中,首先调整基础日志模块,去除掉终端输出并新增 UI 输出
import sys | |
from loguru import logger | |
logger.configure (handlers=[ | |
{ | |
"sink": sys.stderr, | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra [module]}</> | - <lvl>{message}</>", | |
"colorize": True | |
}, | |
{ | |
"sink": "app.log", | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra [module]}</> | - <lvl>{message}</>", | |
"colorize": True | |
} | |
]) | |
def get_logger (module_name: str = "default"): | |
return logger.bind (module=module_name) | |
def add_gui_sink (widget): | |
logger.add (widget.append, format="{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra [module]}</> | - <lvl>{message}</>", level="INFO") | |
# 导出 get_logger 和 add_gui_sink 函数供其他模块使用 | |
__all__ = ["get_logger", "add_gui_sink"] |
随后,在 Qt 中,只需要使用 self.logger.info (message)
即可让日志信息输出到 UI 中,示例如下
from PySide6.QtWidgets import QApplication, QMainWindow, QPushButton, QVBoxLayout, QWidget, QLineEdit, QTextEdit | |
from loguru_base import get_logger, add_gui_sink | |
class MainWindow (QMainWindow): | |
def __init__(self): | |
super ().__init__() | |
self.setWindowTitle ("Simple PySide6 App") | |
# 设置初始大小 | |
self.resize (600, 600) | |
layout = QVBoxLayout () | |
self.button = QPushButton ("Click Me") | |
self.text_input = QLineEdit () | |
self.text_input.setPlaceholderText ("Enter some text here...") | |
self.output_box = QTextEdit () | |
self.output_box.setReadOnly (True) | |
layout.addWidget (self.button) | |
layout.addWidget (self.text_input) | |
layout.addWidget (self.output_box) | |
container = QWidget () | |
container.setLayout (layout) | |
self.setCentralWidget (container) | |
self.bind () | |
# 获取 logger 实例 | |
self.logger = get_logger ("main") | |
# 设置 UI handler | |
add_gui_sink (self.output_box) | |
def bind (self): | |
self.button.clicked.connect (self.on_button_click) | |
self.text_input.returnPressed.connect (self.on_text_input) | |
def on_button_click (self): | |
message = "Button clicked!" | |
self.logger.info (message) | |
def on_text_input (self): | |
text = self.text_input.text () | |
message = f"You entered: {text}" | |
self.logger.info (message) | |
self.text_input.clear () | |
if __name__ == "__main__": | |
app = QApplication ([]) | |
window = MainWindow () | |
window.show () | |
app.exec () |
# 使用装饰器记录函数调用
通过使用装饰器,记录函数的调用和执行时间:
import time | |
import sys | |
from loguru import logger | |
logger.configure (handlers=[ | |
{ | |
"sink": sys.stderr, | |
"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra [module]}</> | - <lvl>{message}</>", | |
"colorize": True | |
} | |
]) | |
def log_function_call (func): | |
def wrapper (*args, **kwargs): | |
start_time = time.time () | |
result = func (*args, **kwargs) | |
end_time = time.time () | |
elapsed_time = end_time - start_time | |
logger.bind (module=func.__name__).info (f"{func.__name__} executed in {elapsed_time:.2f} seconds.") | |
return result | |
return wrapper | |
# 应用装饰器来记录函数调用 | |
@log_function_call | |
def example_function (): | |
# 一些需要记录执行时间的代码 | |
time.sleep (2) | |
# 调用带有装饰器的函数 | |
if __name__ == "__main__": | |
example_function () | |
# 输出示例 | |
>>> 2025-09-24 21:12:50.875 |INFO | __main__ : 装饰器: 19 | example_function | - example_function executed in 2.00 seconds. |