.. raw:: html
<p align="center">
<a href="#readme">
<img alt="Loguru标志" src="https://yellow-cdn.veclightyear.com/835a84d5/7dce1a78-d696-42dc-8020-46aa9e076b65.png">
<!-- 标志致谢: Sambeet from Pixaday -->
<!-- 标志字体: Comfortaa + Raleway -->
</a>
</p>
<p align="center">
<a href="https://pypi.python.org/pypi/loguru"><img alt="Pypi版本" src="https://yellow-cdn.veclightyear.com/835a84d5/7ae80bde-8882-4683-a609-6669f65f143c.svg"></a>
<a href="https://pypi.python.org/pypi/loguru"><img alt="Python版本" src="https://yellow-cdn.veclightyear.com/835a84d5/a9130a85-9379-4e47-ba28-217dafac5f3e.svg"></a>
<a href="https://loguru.readthedocs.io/en/stable/index.html"><img alt="文档" src="https://yellow-cdn.veclightyear.com/835a84d5/473223a2-c6a0-487e-8806-ab3c33d25eb6.svg"></a>
<a href="https://github.com/Delgan/loguru/actions/workflows/tests.yml?query=branch:master"><img alt="构建状态" src="https://img.shields.io/github/actions/workflow/status/Delgan/loguru/tests.yml?branch=master"></a>
<a href="https://codecov.io/gh/delgan/loguru/branch/master"><img alt="覆盖率" src="https://yellow-cdn.veclightyear.com/835a84d5/d435b1b6-374f-42ac-b943-2bd48d1f9ff4.svg"></a>
<a href="https://app.codacy.com/gh/Delgan/loguru/dashboard"><img alt="代码质量" src="https://yellow-cdn.veclightyear.com/835a84d5/2e000dbc-c102-40b7-a19c-0a902f31701f.svg"></a>
<a href="https://github.com/Delgan/loguru/blob/master/LICENSE"><img alt="许可证" src="https://yellow-cdn.veclightyear.com/835a84d5/84408534-7730-4fac-b80d-ccf6031c4dba.svg"></a>
</p>
<p align="center">
<a href="#readme">
<img alt="Loguru标志" src="https://yellow-cdn.veclightyear.com/835a84d5/06dd8827-d971-4b3e-a7f6-bb5f1e203939.gif">
</a>
</p>
=========
Loguru是一个旨在为Python带来愉悦日志记录体验的库。
你是否曾因懒得配置日志记录器而使用print()
代替?...我曾经如此,然而日志记录对每个应用程序都至关重要,并且能够简化调试过程。使用Loguru,你再也没有理由不从一开始就使用日志记录,它就像from loguru import logger
这样简单。
此外,这个库旨在通过添加一系列有用的功能来解决标准日志记录器的缺点,从而使Python日志记录不那么痛苦。在应用程序中使用日志应该是一种自动化行为,Loguru试图使其既愉快又强大。
.. end-of-readme-intro
安装
::
pip install loguru
特性
开箱即用,无需样板代码
_无Handler、无Formatter、无Filter:一个函数统治所有
_更简单的文件日志记录,支持轮转/保留/压缩
_使用大括号风格的现代字符串格式化
_在线程或主程序中捕获异常
_带颜色的美观日志输出
_异步、线程安全、多进程安全
_完全描述性的异常
_根据需要进行结构化日志记录
_昂贵函数的惰性求值
_可自定义的日志级别
_更好的日期时间处理
_适用于脚本和库
_与标准日志完全兼容
_通过环境变量个性化默认设置
_便捷的解析器
_详尽的通知器
_- |strike|
比内置日志记录快10倍
_ |/strike|
功能概览
.. highlight:: python3
.. |logger| replace:: logger
.. _logger: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger
.. |add| replace:: add()
.. _add: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.add
.. |remove| replace:: remove()
.. _remove: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.remove
.. |complete| replace:: complete()
.. _complete: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.complete
.. |catch| replace:: catch()
.. _catch: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.catch
.. |bind| replace:: bind()
.. _bind: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.bind
.. |contextualize| replace:: contextualize()
.. _contextualize: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.contextualize
.. |patch| replace:: patch()
.. _patch: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.patch
.. |opt| replace:: opt()
.. _opt: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.opt
.. |trace| replace:: trace()
.. _trace: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.trace
.. |success| replace:: success()
.. _success: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.success
.. |level| replace:: level()
.. _level: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.level
.. |configure| replace:: configure()
.. _configure: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.configure
.. |disable| replace:: disable()
.. _disable: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.disable
.. |enable| replace:: enable()
.. _enable: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.enable
.. |parse| replace:: parse()
.. _parse: https://loguru.readthedocs.io/en/stable/api/logger.html#loguru._logger.Logger.parse
.. _sinks: https://loguru.readthedocs.io/en/stable/api/logger.html#sink .. _record dict: https://loguru.readthedocs.io/en/stable/api/logger.html#record .. _log messages: https://loguru.readthedocs.io/en/stable/api/logger.html#message .. _easily configurable: https://loguru.readthedocs.io/en/stable/api/logger.html#file .. _markup tags: https://loguru.readthedocs.io/en/stable/api/logger.html#color .. _fixes it: https://loguru.readthedocs.io/en/stable/api/logger.html#time .. _No problem: https://loguru.readthedocs.io/en/stable/api/logger.html#env .. _logging levels: https://loguru.readthedocs.io/en/stable/api/logger.html#levels
.. |better_exceptions| replace:: better_exceptions
.. _better_exceptions: https://github.com/Qix-/better-exceptions
.. |loguru-config| replace:: loguru-config
.. _loguru-config: https://github.com/erezinman/loguru-config
.. |notifiers| replace:: notifiers
.. _notifiers: https://github.com/notifiers/notifiers
开箱即用,无需样板代码 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Loguru
的主要理念是有且仅有一个 |logger|_。
为了方便起见,它被预先配置并默认输出到stderr
(但这完全可以配置)。
::
from loguru import logger
logger.debug("就是这样,简洁优雅的日志记录!")
|logger|_ 只是一个将日志消息分发给配置好的处理程序的接口。很简单,对吧?
无处理程序,无格式化器,无过滤器:一个函数掌控一切 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如何添加处理程序?如何设置日志格式?如何过滤消息?如何设置级别?
一个答案:使用 |add|_ 函数。
::
logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO")
这个函数应该用于注册 sinks_ ,它们负责管理带有 record dict
_ 上下文的 log messages
_ 。sink可以有多种形式:简单的函数、字符串路径、类文件对象、协程函数或内置的Handler。
注意,你也可以使用添加时返回的标识符通过 |remove|_ 移除之前添加的处理程序。如果你想替换默认的 stderr
处理程序,这特别有用:只需调用 logger.remove()
重新开始。
更简便的文件日志记录,支持轮转/保留/压缩 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如果你想将日志消息发送到文件,只需将字符串路径作为sink即可。为方便起见,它还可以自动添加时间戳::
logger.add("file_{time}.log")
如果你需要轮转日志、删除旧日志或在关闭时压缩文件,这也很容易配置。
::
logger.add("file_1.log", rotation="500 MB") # 自动轮转过大的文件
logger.add("file_2.log", rotation="12:00") # 每天中午创建新文件
logger.add("file_3.log", rotation="1 week") # 文件太旧时进行轮转
logger.add("file_X.log", retention="10 days") # 一段时间后清理
logger.add("file_Y.log", compression="zip") # 节省一些珍贵的空间
使用大括号风格的现代字符串格式化 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Loguru
更倾向于使用更优雅强大的 {}
格式化而非 %
,日志函数实际上等同于 str.format()
。
::
logger.info("如果你使用的是Python {}, 当然应该首选{feature}!", 3.6, feature="f-字符串")
捕获线程或主程序中的异常 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
你是否遇到过程序意外崩溃但日志文件中却没有任何记录?你是否注意到线程中发生的异常没有被记录?使用 |catch|_ 装饰器/上下文管理器可以解决这个问题,它确保任何错误都能正确传播到 |logger|_ 。
::
@logger.catch
def my_function(x, y, z):
# 出错了?无论如何都会被捕获!
return 1 / (x + y + z)
带颜色的美观日志 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如果你的终端支持,Loguru
会自动为日志添加颜色。你可以在sink格式中使用 markup tags
_ 来定义你喜欢的样式。
::
logger.add(sys.stdout, colorize=True, format="<green>{time}</green> <level>{message}</level>")
异步、线程安全、多进程安全 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
默认情况下,添加到 |logger|_ 的所有sink都是线程安全的。它们不是多进程安全的,但你可以使用 enqueue
参数来确保日志的完整性。如果你想要异步日志记录,也可以使用这个参数。
::
logger.add("somefile.log", enqueue=True)
作为sink的协程函数也受支持,应该使用 |complete|_ 来等待。
完整描述的异常 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
记录代码中发生的异常对于跟踪bug很重要,但如果你不知道为什么失败,这就没什么用。Loguru
通过允许显示整个堆栈跟踪(包括变量值)来帮助你识别问题(感谢 |better_exceptions|_ !)。
代码::
# 注意,"diagnose=True"是默认值,在生产环境中可能会泄露敏感数据
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("什么?!")
nested(0)
会产生以下结果:
.. code-block:: none
2018-07-17 01:38:43.975 | ERROR | __main__:nested:10 - 什么?!
Traceback (most recent call last):
File "test.py", line 12, in <module>
nested(0)
└ <function nested at 0x7f5c755322f0>
> File "test.py", line 8, in nested
func(5, c)
│ └ 0
└ <function func at 0x7f5c79fc2e18>
File "test.py", line 4, in func
return a / b
│ └ 0
└ 5
ZeroDivisionError: division by zero
注意,由于无法获取帧数据,此功能在默认的Python REPL中不起作用。
另请参阅: 使用Loguru时的安全考虑 <https://loguru.readthedocs.io/en/stable/resources/recipes.html#security-considerations-when-using-loguru>
_.
根据需要进行结构化日志记录 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
想要序列化你的日志以便于解析或传递?使用 serialize
参数,每条日志消息都会在发送到配置的sink之前转换为JSON字符串。
::
logger.add(custom_sink_function, serialize=True)
使用 |bind|_ 可以通过修改 extra
记录属性来为你的logger消息添加上下文。
::
logger.add("file.log", format="{extra[ip]} {extra[user]} {message}")
context_logger = logger.bind(ip="192.168.0.1", user="someone")
context_logger.info("轻松为你的logger添加上下文")
context_logger.bind(user="someone_else").info("内联绑定额外属性")
context_logger.info("在格式化时使用kwargs添加上下文: {user}", user="anybody")
可以使用 |contextualize|_ 临时修改上下文本地状态:
::
with logger.contextualize(task=task_id):
do_something()
logger.info("任务结束")
你还可以通过结合使用 |bind|_ 和 filter
来对日志进行更精细的控制:
::
logger.add("special.log", filter=lambda record: "special" in record["extra"])
logger.debug("此消息不会记录到文件中")
logger.bind(special=True).info("但是,这条消息会记录到文件中!")
最后,|patch|_ 方法允许将动态值附加到每条新消息的记录字典中:
::
logger.add(sys.stderr, format="{extra[utc]} {message}")
logger = logger.patch(lambda record: record["extra"].update(utc=datetime.utcnow()))
昂贵函数的延迟求值 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
有时你想记录详细信息而不影响生产环境的性能,你可以使用 |opt|_ 方法来实现这一点。
::
logger.opt(lazy=True).debug("如果sink级别 <= DEBUG: {x}", x=lambda: expensive_function(2**64))
# 顺便说一下,"opt()"有多种用途
logger.opt(exception=True).info("错误堆栈跟踪添加到日志消息中(也接受元组)")
logger.opt(colors=True).info("每条消息的<blue>颜色</blue>")
logger.opt(record=True).info("显示记录中的值(例如 {record[thread]})")
logger.opt(raw=True).info("绕过sink格式化\n")
logger.opt(depth=1).info("使用父栈上下文(在包装函数中很有用)")
logger.opt(capture=False).info("关键字参数不会添加到{dest}字典中", dest="extra")
可自定义的级别 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Loguru
带有所有标准 logging levels
_ ,并额外添加了 |trace|_ 和 |success|_ 。需要更多级别?那么,只需使用 |level|_ 函数创建即可。
::
new_level = logger.level("SNAKY", no=38, color="<yellow>", icon="🐍")
logger.log("SNAKY", "开始吧!")
更好的日期时间处理
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
标准日志记录的参数过于臃肿,如 datefmt
、msecs
、%(asctime)s
和 %(created)s
,还有不带时区信息的原生日期时间,以及不直观的格式化等。Loguru
解决了这些问题:
::
logger.add("file.log", format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}")
适用于脚本和库 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
在脚本中使用日志记录器很简单,你可以在开始时进行配置。在库中使用 Loguru
时,记住永远不要调用 |add|,而是使用 |disable|,这样日志函数就会变成无操作。如果开发者希望查看你的库的日志,他们可以再次 |enable|_ 它。
::
# 对于脚本
config = {
"handlers": [
{"sink": sys.stdout, "format": "{time} - {message}"},
{"sink": "file.log", "serialize": True},
],
"extra": {"user": "someone"}
}
logger.configure(**config)
# 对于库,应该是你的库的 `__name__`
logger.disable("my_library")
logger.info("无论添加了什么接收器,这条消息都不会显示")
# 在你的应用程序中,启用库中的日志记录器
logger.enable("my_library")
logger.info("这条消息会传播到接收器")
为了更加方便,你还可以使用 |loguru-config|_ 库直接从配置文件设置 logger
。
完全兼容标准日志记录 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
想要使用内置的日志记录 Handler
作为 Loguru
的接收器吗?
::
handler = logging.handlers.SysLogHandler(address=('localhost', 514))
logger.add(handler)
需要将 Loguru
消息传播到标准 logging
吗?
::
class PropagateHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
logging.getLogger(record.name).handle(record)
logger.add(PropagateHandler(), format="{message}")
想要拦截标准 logging
消息并发送到你的 Loguru
接收器吗?
::
class InterceptHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
# 获取对应的 Loguru 级别(如果存在)
level: str | int
try:
level = logger.level(record.levelname).name
except ValueError:
level = record.levelno
# 找到产生日志消息的调用者
frame, depth = inspect.currentframe(), 0
while frame and (depth == 0 or frame.f_code.co_filename == logging.__file__):
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)
通过环境变量个性化默认设置 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
不喜欢默认的日志记录器格式?想要更改 DEBUG
的颜色?没问题:
::
# Linux / OSX
export LOGURU_FORMAT="{time} | <lvl>{message}</lvl>"
# Windows
setx LOGURU_DEBUG_COLOR "<green>"
便捷的解析器 ^^^^^^^^^^^^^^^^^
从生成的日志中提取特定信息通常很有用,这就是为什么 Loguru
提供了 |parse|_ 方法,它有助于处理日志和正则表达式。
::
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # 带命名组的正则表达式
caster_dict = dict(time=dateutil.parser.parse, level=int) # 转换匹配组
for groups in logger.parse("file.log", pattern, cast=caster_dict):
print("解析结果:", groups)
# {"level": 30, "message": "日志示例", "time": datetime(2018, 12, 09, 11, 23, 55)}
全面的通知器 ^^^^^^^^^^^^^^^^^^^
Loguru
可以轻松与优秀的 |notifiers|_ 库(需要单独安装)结合使用,在程序意外失败时接收电子邮件,或发送其他多种类型的通知。
::
import notifiers
params = {
"username": "you@gmail.com",
"password": "abc123",
"to": "dest@gmail.com"
}
# 发送单个通知
notifier = notifiers.get_notifier("gmail")
notifier.notify(message="应用程序正在运行!", **params)
# 对每条错误消息发出警报
from notifiers.logging import NotificationHandler
handler = NotificationHandler("gmail", defaults=params)
logger.add(handler, level="ERROR")
|strike|
比内置日志记录快10倍 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|/strike|
尽管在大多数情况下日志记录对性能的影响可以忽略不计,但零成本的日志记录器可以让我们在任何地方使用它而无需太多顾虑。在即将发布的版本中,Loguru 的关键功能将用 C 语言实现,以达到最高的速度。
文档
API 参考 <https://loguru.readthedocs.io/en/stable/api/logger.html>
_帮助与指南 <https://loguru.readthedocs.io/en/stable/resources.html>
_类型提示 <https://loguru.readthedocs.io/en/stable/api/type_hints.html>
_贡献指南 <https://loguru.readthedocs.io/en/stable/project/contributing.html>
_许可证 <https://loguru.readthedocs.io/en/stable/project/license.html>
_更新日志 <https://loguru.readthedocs.io/en/stable/project/changelog.html>
_