logging配置起来十分的繁琐,感觉并不是一个优雅的实现方式。
有需求就有动力啊,这不,就有人实现了这么一个库,叫做 loguru,可以将 log 的配置和使用更加简单和方便。
使用 pip 安装即可,Python 3 版本的安装如下:
pip3 install loguru
logurn这里直接实例化logger就可以了,但logging需要手动配置才行,从这里就可以看出logurn比logging优雅太多了
from loguru import logger logger.debug('This is debug information') logger.info('This is info information') logger.warning('This is warn information') logger.error('This is error information')
loguru 在输出的不同级别信息时,带上了不同的颜色,使得结果更加直观,其中也包含了时间、级别、模块名、行号以及日志信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mPBZfiIk-1636610434781)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211111100934418.png)]
loguru 中不同日志级别与日志记录方法对应关系 如下:
级别名称 | 严重指数 | 记录器方法 |
---|---|---|
TRACE | 5 | logger.trace() |
DEBUG | 10 | logger.debug() |
INFO | 20 | logger.info() |
SUCCESS | 25 | logger.success() |
WARNING | 30 | logger.warning() |
ERROR | 40 | logger.error() |
CRITICAL | 50 | logger.critical() |
在logging中我们需要手动配置Handler,但在loggurn中我们只需要调用logger.add()方法就可以了.
logger 默认采用 sys.stderr 标准错误输出将日志输出到控制台中,假如想要将日志同时输出到其他的位置,比如日志文件,此时我们只需要使用一行代码即可实现。
logger默认日志输出等级是debug
from loguru import logger # 这里的rotation是一个分割日志,当日志文件达到500MB的时候他会重新创建一个新的文件 # enqueue是一个多进程安全什么什么东西,主要应用在多个进程同时写入一个日志文件 # retention是日志保留时间 logger.add("test.log",rotation="500MB", encoding="utf-8", enqueue=True, retention="10 days") logger.info('This is info information')
当rotation满足要求的时候就自动将日志文件保存为文件名+日期+.log的格式保存
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rvccoXzU-1636610434785)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20211111102601049.png)]
loguru 在输出 日志的时候,还提供了非常灵活的字符串格式化输出日志的功能,如下:
import platform from loguru import logger rounded_value = round(0.345, 2) trace= logger.add('2021-3-28.log') logger.info('If you are using Python {version}, prefer {feature} of course!', version=platform.python_version(), feature='f-strings') # 执行上述代码,输出结果为 2021-03-28 13:43:26.232 | INFO | __main__:<module>:9 - If you are using Python 3.7.6, prefer f-strings of course!
如果当接收器(sink)是文件路径( pathlib.Path )时,可以应用下列参数,同时add() 会返回与所添加的接收器相关联的标识符:
rotation:分隔日志文件,何时关闭当前日志文件并启动一个新文件的条件,;例如,"500 MB"、"0.5 GB"、"1 month 2 weeks"、"10h"、"monthly"、"18:00"、"sunday"、"monday at 18:00"、"06:15"
retention (str, int, datetime.timedelta or callable, optional) ,可配置旧日志的最长保留时间,例如,"1 week, 3 days"、"2 months"
compression (str or callable, optional) :日志文件在关闭时应转换为的压缩或归档格式,例如,"gz"、"bz2"、"xz"、"lzma"、"tar"、"tar.gz"、"tar.bz2"、"tar.xz"、"zip"
delay (bool, optional):是否应该在配置了接收器之后立即创建文件,或者延迟到第一个记录的消息。默认为' False '。
mode (str, optional) :与内置open()函数一样的打开模式。默认为' "a"(以附加模式打开文件)。
buffering (int, optional) :内
置open()函数的缓冲策略,它默认为1(行缓冲文件)。
encoding (str, optional) :文件编码与内置的' open() '函数相同。如果' None ',它默认为'locale.getpreferredencoding() 。
format (str or callable, optional) :格式化模块
属性
key | Description |
---|---|
elapsed | 从程序开始经过的时间差 |
exception | 格式化异常(如果有),否则为' None ' |
extra | 用户绑定的属性字典(参见bind()) |
file | 进行日志记录调用的文件 |
function | 进行日志记录调用的函数 |
level | 用于记录消息的严重程度 |
line | 源代码中的行号 |
message | 记录的消息(尚未格式化) |
module | 进行日志记录调用的模块 |
name | 进行日志记录调用的__name__ |
process | 进行日志记录调用的进程名 |
thread | 进行日志记录调用的线程名 |
time | 发出日志调用时的可感知的本地时间 |
通过 extra bind() 添加额外属性来为结构化日志提供更多属性信息,如下:
from loguru import logger def format_log(): """ Returns: """ trace = logger.add('2021-3-28.log', format="{time:YYYY-MM-DD HH:mm:ss} {extra[ip]} {extra[username]} {level} From {module}.{function} : {message}") extra_logger = logger.bind(ip="192.168.0.1", username="张三") extra_logger.info('This is info information') extra_logger.bind(username="李四").error("This is error information") extra_logger.warning('This is warn information') if __name__ == '__main__': format_log()
如下,我们可以看到在 2021-3-28.log 日志文件中,看到日志按上述模板记录,如下:
2021-03-28 16:27:11 192.168.0.1 张三 INFO From 2021-3-28.format_log : This is info information 2021-03-28 16:27:11 192.168.0.1 李四 ERROR From 2021-3-28.format_log : This is error information 2021-03-28 16:27:11 192.168.0.1 张三 WARNING From 2021-3-28.format_log : This is warn information
level 配置日志最低日志级别
from loguru import logger trace = logger.add('2021-3-29.log', level='ERROR')
rotation 配置日志滚动记录的机制
我们想周期性的创建日志文件,或者按照文件大小自动分隔日志文件,我们可以直接使用 add 方法的 rotation 参数进行配置。
例如,每 200MB 创建一个日志文件,避免每个 log 文件过大,如下:
from loguru import logger trace = logger.add('2021-3-28.log', rotation="200 MB")
例如,每天6点 创建一个日志文件,如下:
from loguru import logger trace = logger.add('2021-3-28.log', rotation='06:00')
例如,每隔2周创建一个 日志文件,如下:
from loguru import logger trace = logger.add('2021-3-28.log', rotation='2 week')
retention 配置日志保留机制
通常,一些久远的日志文件,需要周期性的去清除,避免日志堆积,浪费存储空间。我们可以通过add方法的 retention 参数可以配置日志的最长保留时间。
例如,设置日志文件最长保留 7 天,如下:
from loguru import logger trace = logger.add('2021-3-28.log', retention='7 days')
compression 配置日志压缩格式
loguru 还可以配置文件的压缩格式,比如使用 zip 文件格式保存,示例如下:
from loguru import logger trace = logger.add('2021-3-28.log', compression='zip')
serialize 日志序列化
如果我们希望输出类似于Json-line格式的结构化日志,我们可以通过 serialize 参数,将日志信息序列化的json格式写入log 文件,最后可以将日志文件导入类似于
MongoDB、ElasticSearch 中用作后续的日志分析,代码示例如下:
from loguru import logger import platform trace= logger.add('2021-3-28.log', serialize=True) logger.info('If you are using Python {version}, prefer {feature} of course!', version=platform.python_version(), feature='f-strings')
{ "text": "2021-03-28 13:44:17.104 | INFO | __main__:<module>:9 - If you are using Python 3.7.6, prefer f-strings of course!\n", "record": { "elapsed": { "repr": "0:00:00.010911", "seconds": 0.010911 }, "exception": null, "extra": { "version": "3.7.6", "feature": "f-strings" }, "file": { "name": "2021-3-28.py", "path": "F:/code/MOC/2021-3-28.py" }, "function": "<module>", "level": { "icon": "\u2139\ufe0f", "name": "INFO", "no": 20 }, "line": 9, "message": "If you are using Python 3.7.6, prefer f-strings of course!", "module": "2021-3-28", "name": "__main__", "process": { "id": 22604, "name": "MainProcess" }, "thread": { "id": 25696, "name": "MainThread" }, "time": { "repr": "2021-03-28 13:44:17.104522+08:00", "timestamp": 1616910257.104522 } } }
Traceback 记录(异常追溯)
对于日志而言,没有错误堆栈的日志是没有灵魂的。Loguru 允许显示整个堆栈信息来帮助你发现问题(包括变量)。
loguru集成了一个名为 better_exceptions 的库,不仅能够将异常和错误记录,并且还能对异常进行追溯,如下,我们通过数学运算来举例
通过catch装饰器的方式实现异常捕获,代码示例如下:
from loguru import logger logger.add("2021-3-28.log", backtrace=True, diagnose=True) def func(a, b): return a / b @logger.catch def nested(c): func(5, c) nested(0)
运行上述代码,我们可以发现loguru输出的 Traceback 日志信息, Traceback 日志信息中同时输出了当时的变量值,同时也会出入到日志文件中,如下:
2021-11-11 12:53:37.125 | ERROR | __main__:<module>:19 - An error has been caught in function '<module>', process 'MainProcess' (17236), thread 'MainThread' (12668): Traceback (most recent call last): > File "F:\mygits\123\11.py", line 19, in <module> nested(0) └ <function nested at 0x03A3E4F0> File "F:\mygits\123\11.py", line 16, in nested func(5, c) │ └ 0 └ <function func at 0x01AB1D18> File "F:\mygits\123\11.py", line 11, in func return a / b │ └ 0 └ 5
通过装饰器来对异常进行追溯,会有一个不太好的地方,就是自由度不够。他是将函数里面的所有异常都进行追溯。但是有时候我们只是想对某一块代码进行追溯。
这时我们就通过 logger.exception 来实现异常的捕获与记录。
logger.add("out.log", backtrace=True, diagnose=True) # Caution, may leak sensitive data in prod def func(a, b): return a / b def nested(c): try: func(5, c) except ZeroDivisionError: logger.exception("What?!") nested(0)