loguru
参考: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}输出到标准错误流{"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.