Python日志库:Loguru
Loguru是一个旨在为Python带来愉快的日志记录的库。使用Loguru,没有理由不从一开始就使用日志记录,这就像从Loguru导入日志一样简单from loguru import logger。此外,这个库通过添加一系列有用的功能来解决使用标准日志记录库的痛苦。在应用程序中使用日志应该是自动的,Loguru试图使其既令人愉快又强大。
安装
1 | pip install loguru |
特性
开箱即用
Loguru的主要概念是有且只需要一个Logger。为了方便起见,它预先配置从输出到stderr开始(但是这是完全可配置的)。
1 | from loguru import logger |
Logger只是一个将日志消息分发给已配置的处理程序的接口。
无处理程序、无格式化、无过滤器:一个函数来规定所有
如何添加处理程序? 如何设置日志格式? 如何过滤消息? 如何设置级别?答案是:add()函数。
1 | logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO") |
此函数用来注册使用record dict来管理log messages上下文的sinks。sink可以有多种形式:一个简单的函数、一个字符串路径、一个类似文件的对象、一个协程函数或一个内置的Handler。
注意,还可以通过使用在添加处理程序时返回的标识符来remove()以前添加的处理程序。如果希望取代默认的stderr处理程序,这尤其有用:只需调用logger.remove()来重新开始。
更容易的旋转/保留/压缩日志文件
如果将记录的消息发送到文件,只需使用字符串路径作为接收器。为了方便起见,它还可以自动计时:
1 | logger.add("file_{time}.log") |
如果需要旋转日志文件,或者删除较旧的日志文件,或者在关闭时压缩日志文件,也是很容易配置的。
1 | logger.add("file_1.log", rotation="500 MB") # Automatically rotate too big file |
大括号样式的现代字符串格式设置
Loguru喜欢更优雅和强大的{}格式化而不是%,日志记录函数实际上等效于str.format ()。
1 | logger.info("If you're using Python {}, prefer {feature} of course!", 3.6, feature="f-strings") |
在线程或主线程中捕获异常
您是否曾经遇到过程序意外崩溃,而在日志文件中没有看到任何信息?您是否注意到线程中发生的异常没有被记录?可以使用catch ()装饰器/上下文管理器解决这个问题,该管理器确保任何错误都被正确地传递到logger。
1 |
|
优美的彩色日志
如果终端兼容,Loguru会自动为日志添加颜色。可以在接收器格式中用markup tags来定义喜欢的样式。
1 | logger.add(sys.stdout, colorize=True, format="<green>{time}</green> <level>{message}</level>") |
异步、线程安全、多进程安全
默认情况下,添加到logger的所有接收器都是线程安全的。它们不是多进程安全的,但是可以对消息进行enqueue以确保日志的完整性。如果需要异步日志记录,也可以使用同样的参数。
1 | logger.add("somefile.log", enqueue=True) |
也支持作为接收器的协同函数,并且应该使用complete()进行等待。
完全描述例外
记录代码中发生的异常对于跟踪bug非常重要,但是如果不知道为什么会失败,那么记录异常就完全没有用处。Loguru允许显示整个堆栈跟踪,包括变量的值,从而帮助识别问题(感谢better_exception!)。
代码如下:
1 | # Caution, "diagnose=True" is the default and may leak sensitive data in prod |
结果如下:
1 | 2018-07-17 01:38:43.975 | ERROR | __main__:nested:10 - What?! |
注意,由于帧数据不可用,这个特性不能在默认的Python REPL上工作。参考:Security considerations when using Loguru。
根据需要结构化日志
是否希望将日志序列化以便于解析或传递它们?使用serialize序列化参数,每个日志消息在发送到配置的接收器之前都将转换为JSON字符串。
1 | logger.add(custom_sink_function, serialize=True) |
通过使用bind(),可以通过修改额外的record属性添加消息上下文。
1 | logger.add("file.log", format="{extra[ip]} {extra[user]} {message}") |
可以使用contextualize()临时修改上下文局部状态:
1 | with logger.contextualize(task=task_id): |
通过组合bind()和filter,可以对日志进行更细粒度的控制:
1 | logger.add("special.log", filter=lambda record: "special" in record["extra"]) |
最后,patch()方法允许将动态值附加到每条新消息记录:
1 | logger.add(sys.stderr, format="{extra[utc]} {message}") |
延迟求值代价函数
有时希望在生产环境中记录详细信息而不会影响性能,可以使用opt()方法来实现这一点。
1 | logger.opt(lazy=True).debug("If sink level <= DEBUG: {x}", x=lambda: expensive_function(2**64)) |
自定义级别
Loguru提供了所有添加trace()和success()的标准日志记录级别。是否需要更多?然后,使用level()函数创建它。
1 | new_level = logger.level("SNAKY", no=38, color="<yellow>", icon="🐍") |
更好的处理日期时间
标准日志记录中充斥着诸如datefmt或msecs、%(asctime)s和%(created)s之类的参数,没有时区信息的朴素日期时间,没有直观的格式设置等等。Loguru解决了这个问题:
1 | logger.add("file.log", format="{time:YYYY-MM-DD at HH:mm:ss} | {level} | {message}") |
适用于脚本和库
在脚本中使用日志记录器很容易,并且可以在开始时configure()。要在库中使用Loguru,请记住永远不要调用add(),而是使用disable(),这样日志记录函数就变为no-op。如果开发人员希望查看库的日志,他们可以再次enable()。
1 | # For scripts |
为了更加方便,还可以使用loguru-config库直接从配置文件设置日志记录器。
与标准日志记录完全兼容
是否希望使用内置的日志Handler作为Loguru接收器?
1 | handler = logging.handlers.SysLogHandler(address=('localhost', 514)) |
是否需要将Loguru消息传播到标准日志?
1 | class PropagateHandler(logging.Handler): |
是否想要拦截到Loguru接收器的标准日志消息?
1 | class InterceptHandler(logging.Handler): |
通过环境变量的个性化默认值
是否不喜欢默认的日志记录器格式?喜欢另一种DEBUG颜色?没问题:
1 | # Linux / OSX |
方便的解析器
从生成的日志中提取特定信息通常很有用,这就是Loguru提供parse()方法帮助处理日志和正则表达式的原因。
1 | pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # Regex with named groups |
详尽的通知
Loguru可以很容易地与一流的notifiers库(必须单独安装)组合在一起,以便在程序意外失败时接收电子邮件或发送许多其他类型的通知。
1 | import notifiers |