酷炫的python日志库-loguru
Loguru是一个python的日志库,比logging更简单,好用,功能丰富。
GitHub - Delgan/loguru: Python logging made (stupidly) simple
安装
pip install loguru
特征
- 开箱即用,补样板
- 没有处理程序,没有清理程序,没有过滤器:一个函数可以统治所有这些
- 通过旋转/保留/压缩更轻松地记录文件
- 使用大字符串样式的现代字符串格式
- 在线程或主线程中捕获异常
- 漂亮的颜色记录
- 异步、线程安全、多进程安全
- 完整描述性异常
- 根据需要形成日志记录
- 昂贵费用的评估
- 可定制的级别
- 更好的日期时间处理
- 适用于脚本和库
- 与标准日志记录完全兼容
- 通过环境变量进行个性化默认设置
- 方便的解析器
- 观赏的通知程序
比内置日志记录快 10 倍
参观游乐场
开箱即用,补样板
Loguru 的主要概念只有一个 。logger
为了方便入门,它是预先配置的并且从一stderr
开始就输出(但这是完全可配置的)。
from loguru import loggerlogger.debug("That's it, beautiful and simple logging!")
这只是一个将日志消息分派到配置的处理程序的接口。很简单,对吧?logger
没有处理程序,没有清理程序,没有过滤器:一个函数可以统治所有这些
如何添加处理程序?如何设置日志格式?如何过滤消息?如何设置水平?
答案之一:功能。add()
logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO")
此函数评估注册负责管理与记录字典上下文相关的日志消息的接收器。接收器可以采用多种形式:简单函数、字符串路径、类文件对象、协程函数或内置处理程序。
请注意,您还可以使用添加时返回的标识符来添加先前添加的处理程序。如果您想替换默认处理程序,这特别有用:只需调用即可重新开始。remove()stderr
logger.remove()
通过旋转/保留/压缩更轻松地记录文件
如果最多记录的消息发送到文件,只需使用字符串路径作为接收器。为了方便起见,它也可以自动解析:
logger.add("file_{time}.log")
如果您需要旋转记录器,如果您想删除较旧的日志,或者如果您希望在关闭时压缩文件,它也可以轻松配置。
logger.add("file_1.log", rotation="500 MB") # Automatically rotate too big file
logger.add("file_2.log", rotation="12:00") # New file is created each day at noon
logger.add("file_3.log", rotation="1 week") # Once the file is too old, it's rotatedlogger.add("file_X.log", retention="10 days") # Cleanup after some timelogger.add("file_Y.log", compression="zip") # Save some loved space
使用大字符串样式的现代字符串格式
Loguru 更喜欢更优雅和更强大的{}
格式%
,日志记录功能实际上实际上str.format()
。
logger.info("If you're using Python {}, prefer {feature} of course!", 3.6, feature="f-strings")
在线程或主线程中捕获异常
您是否曾遇到过您的程序意外崩溃而在日志文件中看不到任何内容?您是否注意到线程中发生的异常没有被记录?这可以使用装饰器/上下文管理器来解决,它确保任何错误都传播到正确的地方。catch()logger
@logger.catch
def my_function(x, y, z):# An error? It's caught anyway!return 1 / (x + y + z)
漂亮的颜色记录
如果您的兼容,Loguru 会自动为您的日志添加颜色。您可以通过使用接收器格式的标记来定义您喜欢的样式。
logger.add(sys.stdout, colorize=True, format="<green>{time}</green> <level>{message}</level>")
异步、线程安全、多进程安全
默认情况下,添加到的所有接收器都是线程安全的。它们不是多进程安全的,但您可以通过消息来确保日志的限制。如果您想要异步日志记录,也可以使用相同的参数。loggerenqueue
logger.add("somefile.log", enqueue=True)
首先接收器的协程函数也受支持,并且应该使用等待。complete()
完整描述性异常
记录代码中的异常对于跟踪错误非常重要,但如果您不知道失败的原因,那么它就没用处。Loguru 通过允许显示整个堆栈(跟踪包括标记值)来帮助您识别问题(感谢您!) 。better_exceptions
代码:
logger.add("out.log", backtrace=True, diagnose=True) # Caution, may leak sensitive data in proddef func(a, b):return a / bdef nested(c):try:func(5, c)except ZeroDivisionError:logger.exception("What?!")nested(0)
会导致:
2018-07-17 01:38:43.975 |错误| __main__: 演讲:10 - 什么?!
回溯(最近一次调用最后一次):文件“test.py”,第12行,位于 <module> 中演讲(0)└ <函数调用在 0x7f5c755322f0>> 文件“test.py”,第8行,曼哈顿函数(5,c)│ └ 0└ <0x7f5c79fc2e18 处的函数 func>文件“test.py”,第4行,在 func 中返回a/b│ └ 0└ 5ZeroDivisionError:除以零
请注意,由于帧数据不可用,此功能不适用于默认的Python REPL。
根据需要形成日志记录
想要对您的日志进行序列化以便解析或传送它们吗?使用该serialize
参数,每条日志消息在发送到配置的接收器之前都将转换为 JSON 字符串。
logger.add(custom_sink_function, serialize=True)
bind()您可以通过修改附加的记录属性来将记录器消息放在上下文中。
logger.add("file.log", format="{extra[ip]} {extra[user]} {message}")
context_logger = logger.bind(ip="192.168.0.1", user="someone")
context_logger.info("Contextualize your logger easily")
context_logger.bind(user="someone_else").info("Inline binding of extra attribute")
context_logger.info("Use kwargs to add context during formatting: {user}", user="anybody")
可以使用以下命令临时修改上下文本地状态:contextualize()
with logger.contextualize(task=task_id):do_something()logger.info("End of task")
bind()您还可以通过组合和来对日志进行更细粒度的控制filter
:
logger.add("special.log", filter=lambda record: "special" in record["extra"])
logger.debug("This message is not logged to the file")
logger.bind(special=True).info("This message, though, is logged to the file!")
最后,该方法允许将动态值附加到每条新消息的记录字典中: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("If sink level <= DEBUG: {x}", x=lambda: expensive_function(2**64))# By the way, "opt()" serves many usages
logger.opt(exception=True).info("Error stacktrace added to the log message (tuple accepted too)")
logger.opt(colors=True).info("Per message <blue>colors</blue>")
logger.opt(record=True).info("Display values from the record (eg. {record[thread]})")
logger.opt(raw=True).info("Bypass sink formatting\n")
logger.opt(depth=1).info("Use parent stack context (useful within wrapped functions)")
logger.opt(capture=False).info("Keyword arguments not added to {dest} dict", dest="extra")
可定制的级别
Loguru 附带了添加和的所有标准日志记录级别。您还需要更多吗?然后,只需使用该函数创建它即可。trace()success()level()
new_level = logger.level("SNAKY", no=38, color="<yellow>", icon="🐍")logger.log("SNAKY", "Here we go!")
更好的日期时间处理
标准日志记录因datefmt
或msecs
、%(asctime)s
和%(created)s
、没有时区信息的简单日期时间、不解读的格式等参数而构建聚合。Loguru修复了它:
logger.add("file.log", format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}")
适用于脚本和库
在脚本中使用记录器很简单,您可以从一开始就使用它。要从库内部使用 Loguru,请记住永远不要调用相反的使用,以便日志记录函数设为无操作。如果开发人员希望查看您的库房日志,他们可以再次查看。configure()add()disable()enable()
# For scripts
config = {"handlers": [{"sink": sys.stdout, "format": "{time} - {message}"},{"sink": "file.log", "serialize": True},],"extra": {"user": "someone"}
}
logger.configure(**config)# For libraries, should be your library's `__name__`
logger.disable("my_library")
logger.info("No matter added sinks, this message is not displayed")# In your application, enable the logger in the library
logger.enable("my_library")
logger.info("This message however is propagated to the sinks")
为了更加方便,您还可以使用该库直接从配置文件进行设置。loguru-configlogger
与标准日志记录完全兼容
希望使用内置日志记录Handler
作为 Loguru 接收器?
handler = logging.handlers.SysLogHandler(address=('localhost', 514))
logger.add(handler)
需要将Loguru消息传播到标准日志记录吗?
class PropagateHandler(logging.Handler):def emit(self, record):logging.getLogger(record.name).handle(record)logger.add(PropagateHandler(), format="{message}")
想要拦截发送至Loguru接收器的标准日志消息吗?
class InterceptHandler(logging.Handler):def emit(self, record):# Get corresponding Loguru level if it exists.try:level = logger.level(record.levelname).nameexcept ValueError:level = record.levelno# Find caller from where originated the logged message.frame, depth = sys._getframe(6), 6while frame and frame.f_code.co_filename == logging.__file__:frame = frame.f_backdepth += 1logger.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>.*)" # Regex with named groups
caster_dict = dict(time=dateutil.parser.parse, level=int) # Transform matching groupsfor groups in logger.parse("file.log", pattern, cast=caster_dict):print("Parsed:", groups)# {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}
观赏的通知程序
Loguru 可以轻松地与强大的库(必须单独安装)结合使用,方便在程序意外失败时接收电子邮件或发送许多其他类型的通知。notifiers
import notifiersparams = {"username": "you@gmail.com","password": "abc123","to": "dest@gmail.com"
}# Send a single notification
notifier = notifiers.get_notifier("gmail")
notifier.notify(message="The application is running!", **params)# Be alerted on each error message
from notifiers.logging import NotificationHandlerhandler = NotificationHandler("gmail", defaults=params)
logger.add(handler, level="ERROR")
Python日志库Loguru教程(最人性化的Python日志模块) - 简书