当前位置:   article > 正文

【拓展】Loguru:更为优雅、简洁的Python 日志管理模块

loguru

【拓展】Loguru:更为优雅、简洁的Python 日志管理模块

【一】引入

  • 在 Python 开发中涉及到日志记录,我们或许通常会想到内置标准库 —— logging 。
  • 虽然logging 库采用的是模块化设计,可以设置不同的 handler 来进行组合,但是在配置上较为繁琐。
  • 同时在多线程或多进程的场景下,若不进行特殊处理还会导致日志记录会出现异常。
  • 本文将介绍一个十分优雅、简洁的日志记录第三方库—— loguru ,我们可以通过导入其封装的logger 类的实例,即可直接进行调用。

【二】安装

  • 使用 pip 安装即可,Python 3 版本的安装如下:
pip3 install loguru

【三】基本使用

【1】使用方法

  • 我们直接通过导入loguru 封装好的logger 类的实例化对象,不需要手动创建 logger,直接进行调用不同级别的日志输出方法。

【2】示例

  1. from loguru import logger
  2. logger.debug('This is debug information')
  3. logger.info('This is info information')
  4. logger.warning('This is warn information')
  5. logger.error('This is error information')

【3】样式

  • 在 IDE 或终端运行时会发现
    • loguru 在输出的不同级别信息时,带上了不同的颜色,使得结果更加直观
    • 其中也包含了时间、级别、模块名、行号以及日志信息。

【4】不同日志级别与日志记录方法对应关系

  • loguru 中不同日志级别与日志记录方法对应关系 如下:
级别名称严重度值记录器法
TRACE5logger.trace()
DEBUG10logger.debug()
INFO20logger.info()
SUCCESS25logger.success()
WARNING30logger.warning()
ERROR40logger.error()
CRITICAL50logger.critical()
  • 简单语法
  1. logger.trace('这是一条记录日志)
  2. logger.debug('这是一条测试日志')
  3. logger.info('这是一条信息日志')
  4. logger.success('这是一条成功日志')
  5. logger.warning('这是一条警告日志')
  6. logger.error('这是一条错误日志')
  7. logger.critical('这是一条严重错误日志')

【5】日志规则

  • 设置日志格式,过滤器,日志级别
  1. from loguru import logger
  2. logger.add("file.log", format="{time} {level} {message}", level="INFO")
  3. logger.debug("这是一条测试日志")
  4. logger.info("这是一条信息日志")
  • 终端展示
  1. 2023-08-07 23:16:25.307 | DEBUG | __main__:<module>:18 - 这是一条测试日志
  2. 2023-08-07 23:16:25.307 | INFO | __main__:<module>:19 - 这是一条信息日志

【6】保存到文件

  1. from loguru import logger
  2. logger.add("file.log")
  3. logger.debug("这是一条debug日志")
  4. logger.info("这是一条info日志")
  • 结果为目录多出一个日志文件:file.log

【7】日志文件拆分(add方法)

  • 由于日志文件会非常大,所以实际情况中会对日志文件进行拆分。
  1. from loguru import logger
  2. logger.add("log_{time}.log", encoding='utf-8') # 以时间命名

【四】loguru 配置日志文件

  • logger 默认采用 sys.stderr 标准错误输出将日志输出到控制台中,假如想要将日志同时输出到其他的位置,比如日志文件,此时我们只需要使用一行代码即可实现。
  • 例如,将日志信息输出到 2021-3-28.log 文件中,可以这么写:
  1. from loguru import logger
  2. logger.add("E:/Old Boy/day_projects/日志模块/log_2023-8-7.log",rotation="500MB", encoding="utf-8", enqueue=True, retention="10 days")
  3. logger.info('This is info information')
  • 终端显示结果
2023-08-07 22:47:11.611 | INFO     | __main__:<module>:19 - This is info information
  • 如上,loguru直接通过 add() 方法,完成了日志文件的配置。

【五】日志内容的字符串格式化

  • loguru 在输出 日志的时候,还提供了非常灵活的字符串格式化输出日志的功能,如下:
  1. import platform
  2. from loguru import logger
  3. rounded_value = round(0.345, 2)
  4. trace= logger.add('2023-8-7.log')
  5. logger.info('If you are using Python {version}, prefer {feature} of course!', version=platform.python_version(), feature='f-strings')
  • 执行上述代码,输出结果为
2023-08-07 22:46:03.812 | INFO     | __main__:<module>:21 - If you are using Python 3.9.13, prefer f-strings of course!

【六】loguru日志常用参数配置解析

  • sink:可以传入一个 file 对象(file-like object),或一个 str 字符串或者 pathlib.Path 对象,或一个方法(coroutine function),或 logging 模块的 Handler(logging.Handler)。
  • level (int or str, optional) :应将已记录消息发送到接收器的最低严重级别。
  • format (str or callable, optional) :格式化模块,在发送到接收器之前,使用模板对记录的消息进行格式化。
  • filter (callable, str or dict, optional) :用于决定每个记录的消息是否应该发送到接收器。
  • colorize (bool, optional) – 是否应将格式化消息中包含的颜色标记转换为用于终端着色的Ansi代码,或以其他方式剥离。如果None,根据水槽是否为TTY自动作出选择。
  • serialize (bool, optional) :在发送到接收器之前,记录的消息及其记录是否应该首先转换为JSON字符串。
  • backtrace (bool, optional) :格式化的异常跟踪是否应该向上扩展,超出捕获点,以显示生成错误的完整堆栈跟踪。
  • diagnose (bool, optional) :异常跟踪是否应该显示变量值以简化调试。在生产中,这应该设置为“False”,以避免泄漏敏感数据。
  • enqueue (bool, optional) :要记录的消息在到达接收器之前是否应该首先通过多进程安全队列。当通过多个进程将日志记录到文件中时,这是非常有用的。这还具有使日志调用非阻塞的优点。
  • catch (bool, optional) :是否应该自动捕获接收器处理日志消息时发生的错误。如果True上显示异常消息 sys.stderr。但是,异常不会传播到调用者,从而防止应用程序崩溃。

如果当接收器(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() 。

【七】loguru 日志常用方式

【1】停止日志记录到文件中

  • add 方法 添加 sink 之后我们也可以对其进行删除, 删除的时候根据刚刚 add 方法返回的 id 进行删除即可,还原到标准输出。如下:
  1. from loguru import logger
  2. trace = logger.add('2021-8-7.log')
  3. logger.error('This is error information')
  4. logger.remove(trace)
  5. logger.warning('This is warn information')
  • 终端显示结果
  1. 2023-08-07 22:50:37.834 | ERROR | __main__:<module>:17 - This is error information
  2. 2023-08-07 22:50:37.834 | WARNING | __main__:<module>:20 - This is warn information
  • 日志2023-8-7.log内容如下
2023-08-07 22:50:37.834 | ERROR    | __main__:<module>:17 - This is error information
  • 将 sink 对象移除之后,在这之后的内容不会再输出到日志文件中。
  • 只输出到文本,不在console输出
  • 通过 logger.remove(handler_id=None) 删除以前添加的处理程序,并停止向其接收器发送日志。然后通过add 添加输出日志文件,即可 实现 只输出到文本,不在console输出,如下:
  1. from loguru import logger
  2. # 清除之前的设置
  3. logger.remove(handler_id=None)
  4. trace = logger.add('2023-8-7.log')
  5. logger.error('This is error information')
  6. logger.warning('This is warn information')

【2】filter 配置日志过滤规则

  • 如下,我们通过实现自定义方法error_only,判断日志级别
    • 当日志级别为ERROR,返回TRUE,我们在add方法设置filter参数时,设置为error_only,即可过滤掉ERROR以外的所有日志 。
  1. from loguru import logger
  2. def error_only(record):
  3. """
  4. error 日志 判断
  5. Args:
  6. record:
  7. Returns: 若日志级别为ERROR, 输出TRUE
  8. """
  9. return record["level"].name == "ERROR"
  10. # ERROR以外级别日志被过滤掉
  11. logger.add("2023-8-7.log", filter=error_only)
  12. logger.error('This is error information')
  13. logger.warning('This is warn information')
  • 终端显示
  1. 2023-08-07 22:53:14.679 | ERROR | __main__:<module>:32 - This is error information
  2. 2023-08-07 22:53:14.680 | WARNING | __main__:<module>:33 - This is warn information
  • 2023-8-7.log 日志中,我们可以看到仅记录了ERROR级别日志。
2023-08-07 22:53:14.679 | ERROR    | __main__:<module>:32 - This is error information

【3】format 配置日志记录格式化模板

  1. from loguru import logger
  2. def format_log():
  3. """
  4. Returns:
  5. """
  6. trace = logger.add('2023-8-7.log',
  7. format="{time:YYYY-MM-DD HH:mm:ss} {level} From {module}.{function} : {message}")
  8. logger.warning('This is warn information')
  9. if __name__ == '__main__':
  10. format_log()
  • 终端显示
2023-08-07 22:54:11.901 | WARNING  | __main__:format_log:26 - This is warn information
  • 如下,我们可以看到在 2023-8-7.log 日志文件中,如 "{time:YYYY-MM-DD HH:mm:ss} {level} From {module}.{function} : {message}" 格式模板进行记录:
2023-08-07 22:54:11 WARNING From 01 日志模块测试.format_log : This is warn information

【4】enqueue 异步写入

logger.add("2023-8-7.log", enqueue=True)
  • 使用enqueue,可保证线程安全,多线程安全。

【5】其它的格式化模板属性 如下:

KeyDescription
elapsed从程序开始经过的时间差
exception格式化异常(如果有),否则为' None '
extra用户绑定的属性字典(参见bind())
file进行日志记录调用的文件
function进行日志记录调用的函数
level用于记录消息的严重程度
line源代码中的行号
message记录的消息(尚未格式化)
module进行日志记录调用的模块
name进行日志记录调用的__name__
process进行日志记录调用的进程名
thread进行日志记录调用的线程名
time发出日志调用时的可感知的本地时间

(1)通过 extra bind() 添加额外属性来为结构化日志提供更多属性信息

  • 如下:
  1. from loguru import logger
  2. def format_log():
  3. """
  4. Returns:
  5. """
  6. trace = logger.add('2023-8-7.log',
  7. format="{time:YYYY-MM-DD HH:mm:ss} {extra[ip]} {extra[username]} {level} From {module}.{function} : {message}")
  8. extra_logger = logger.bind(ip="192.168.0.1", username="张三")
  9. extra_logger.info('This is info information')
  10. extra_logger.bind(username="李四").error("This is error information")
  11. extra_logger.warning('This is warn information')
  12. if __name__ == '__main__':
  13. format_log()
  • 终端显示
  1. 2023-08-07 22:56:16.435 | INFO | __main__:format_log:27 - This is info information
  2. 2023-08-07 22:56:16.435 | ERROR | __main__:format_log:28 - This is error information
  3. 2023-08-07 22:56:16.436 | WARNING | __main__:format_log:30 - This is warn information
  • 如下,我们可以看到在 2023-8-7.log 日志文件中,看到日志按上述模板记录,如下:
  1. 2023-08-07 22:56:16 192.168.0.1 张三 INFO From 01 日志模块测试.format_log : This is info information
  2. 2023-08-07 22:56:16 192.168.0.1 李四 ERROR From 01 日志模块测试.format_log : This is error information
  3. 2023-08-07 22:56:16 192.168.0.1 张三 WARNING From 01 日志模块测试.format_log : This is warn information

(2)level 配置日志最低日志级别

  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log', level='ERROR')

(3)rotation 配置日志滚动记录的机制

  • 我们想周期性的创建日志文件,或者按照文件大小自动分隔日志文件,我们可以直接使用 add 方法的 rotation 参数进行配置。
  • 例如,每 200MB 创建一个日志文件,避免每个 log 文件过大,如下:
  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log', rotation="200 MB")
  • 例如,每天6点 创建一个日志文件,如下:
  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log', rotation='06:00')
  • 例如,每隔2周创建一个 日志文件,如下:
  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log', rotation='2 week')
  • 例如,定时清理 日志文件,如下:
  1. from loguru import logger
  2. logger.add("2023-8-7.log", retention="10 days")

(4)retention 配置日志保留机制

  • 通常,一些久远的日志文件,需要周期性的去清除,避免日志堆积,浪费存储空间。
  • 我们可以通过add方法的 retention 参数可以配置日志的最长保留时间。
  • 例如,设置日志文件最长保留 7 天,如下:
  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log', retention='7 days')

(5)compression 配置日志压缩格式

  • loguru 还可以配置文件的压缩格式,比如使用 zip 文件格式保存,示例如下:
  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log', compression='zip')
  • 通过设置compression参数,来选择文件压缩格式,如 zip、tar、gz 等。

(6)serialize 日志序列化

  • 如果我们希望输出类似于Json-line格式的结构化日志,我们可以通过 serialize 参数,将日志信息序列化的json格式写入log 文件,最后可以将日志文件导入类似于MongoDB、ElasticSearch 中用作后续的日志分析,代码示例如下:
  1. from loguru import logger
  2. import platform
  3. rounded_value = round(0.345, 2)
  4. trace= logger.add('2023-8-7.log', serialize=True)
  5. logger.info('If you are using Python {version}, prefer {feature} of course!', version=platform.python_version(), feature = 'f-strings')
  • 终端显示
2023-08-07 23:01:14.356 | INFO     | __main__:<module>:21 - If you are using Python 3.9.13, prefer f-strings of course!
  • 在2023-8-7.log日志文件,我们可以看到每条日志信息都被序列化后存在日志文件中,如下:
  1. {
  2. "text": "2023-08-07 23:01:14.356 | INFO | __main__:<module>:21 - If you are using Python 3.9.13, prefer f-strings of course!\n",
  3. "record": {
  4. "elapsed": {
  5. "repr": "0:00:00.018001",
  6. "seconds": 0.018001
  7. },
  8. "exception": null,
  9. "extra": {
  10. "version": "3.9.13",
  11. "feature": "f-strings"
  12. },
  13. "file": {
  14. "name": "01 日志模块测试.py",
  15. "path": "E:\\day_projects\\日志模块\\01 日志模块测试.py"
  16. },
  17. "function": "<module>",
  18. "level": {
  19. "icon": "\u2139\ufe0f",
  20. "name": "INFO",
  21. "no": 20
  22. },
  23. "line": 9,
  24. "message": "If you are using Python 3.9.13, prefer f-strings of course!",
  25. "module": "01 日志模块测试",
  26. "name": "__main__",
  27. "process": {
  28. "id": 21636,
  29. "name": "MainProcess"
  30. },
  31. "thread": {
  32. "id": 28612,
  33. "name": "MainThread"
  34. },
  35. "time": {
  36. "repr": "2023-08-07 23:01:14.356111+08:00",
  37. "timestamp": 1691420474.356111
  38. }
  39. }
  40. }

(7)Traceback 记录(异常追溯)

  • loguru集成了一个名为 better_exceptions 的库,不仅能够将异常和错误记录,并且还能对异常进行追溯
  • 如下,我们通过在遍历列表的过程中删除列表元素,以触发IndexError 异常,
  • 通过catch装饰器的方式实现异常捕获,代码示例如下:
  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log')
  3. @logger.catch
  4. def index_error(custom_list: list):
  5. for index in range(len(custom_list)):
  6. index_value = custom_list[index]
  7. if custom_list[index] < 2:
  8. custom_list.remove(index_value)
  9. print(index_value)
  10. if __name__ == '__main__':
  11. index_error([1, 2, 3])
  • 运行上述代码,我们可以发现loguru输出的 Traceback 日志信息, Traceback 日志信息中同时输出了当时的变量值,如下:

  • 在 2023-8-7.log 日志文件中也同样输出了上述格式的异常追溯信息,如下。
  1. 2023-08-07 23:08:12.010 | ERROR | __main__:<module>:30 - An error has been caught in function '<module>', process 'MainProcess' (17440), thread 'MainThread' (26612):
  2. Traceback (most recent call last):
  3. > File "E:\Old Boy\day_projects\日志模块\01 日志模块测试.py", line 30, in <module>
  4. index_error([1, 2, 3])
  5. <function index_error at 0x000001DFBB471550>
  6. File "E:\Old Boy\day_projects\日志模块\01 日志模块测试.py", line 22, in index_error
  7. index_value = custom_list[index]
  8. │ └ 2
  9. └ [2, 3]
  10. IndexError: list index out of range
  • 同时,附上对类中的类方法和静态方法的代码实例,以供参考
  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log')
  3. class Demo:
  4. @logger.catch
  5. def index_error(self, custom_list: list):
  6. for index in range(len(custom_list)):
  7. index_value = custom_list[index]
  8. if custom_list[index] < 2:
  9. custom_list.remove(index_value)
  10. @staticmethod
  11. @logger.catch
  12. def index_error_static(custom_list: list):
  13. for index in range(len(custom_list)):
  14. index_value = custom_list[index]
  15. if custom_list[index] < 2:
  16. custom_list.remove(index_value)
  17. if __name__ == '__main__':
  18. # Demo().index_error([1, 2, 3])
  19. Demo.index_error_static([1, 2, 3])

(8)通过 logger.exception 方法也可以实现异常的捕获与记录:

  1. from loguru import logger
  2. trace = logger.add('2023-8-7.log')
  3. def index_error(custom_list: list):
  4. for index in range(len(custom_list)):
  5. try:
  6. index_value = custom_list[index]
  7. except IndexError as err:
  8. logger.exception(err)
  9. break
  10. if custom_list[index] < 2:
  11. custom_list.remove(index_value)
  12. if __name__ == '__main__':
  13. index_error([1, 2, 3])
  • 终端打印
  1. 2023-08-07 23:11:16.587 | ERROR | __main__:index_error:24 - list index out of range
  2. Traceback (most recent call last):
  3. File "E:\Old Boy\day_projects\日志模块\01 日志模块测试.py", line 32, in <module>
  4. index_error([1, 2, 3])
  5. <function index_error at 0x000002B1327E01F0>
  6. > File "E:\Old Boy\day_projects\日志模块\01 日志模块测试.py", line 22, in index_error
  7. index_value = custom_list[index]
  8. │ └ 2
  9. └ [2, 3]
  10. IndexError: list index out of range
  • 运行上述代码,我们可以发现loguru输出的 Traceback 日志信息, Traceback 日志信息中同时输出了当时的变量值,如下:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/953290
推荐阅读
相关标签
  

闽ICP备14008679号