Python 日志记录库 loguru

2023-10-28 13:50
文章标签 python 日志 记录 loguru

本文主要是介绍Python 日志记录库 loguru,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、说明和安装

1、说明
在部署一些定时运行或者长期运行的任务时,为了留存一些导致程序出现异常或错误的信息,通常会才用日志的方式来进行记录这些信息。
在 Python 中用到日志记录,那就不可避免地会用到内置的 logging 标准库 。虽然 logging 库采用的是模块化设计,你可以设置不同的 handler 来进行组合,但是在配置上通常较为繁琐;而且如果不是特别处理,在一些多线程或多进程的场景下使用 logging 还会导致日志记录会出现错乱或是丢失的情况。
但有这么一个库,它不仅能够减少繁琐的配置过程还能实现和 logging 类似的功能,同时还能保证日志记录的线程进程安全,又能够和 logging 相兼容,并进一步追踪异常也能进行代码回溯。这个库叫 loguru —— 一个专为像我这样懒人而生日志记录库。
loguru 库的使用可以说是十分简单,我们直接可以通过导入它本身封装好的 logger 类就可以直接进行调用。

2、安装

#!/usr/bin/env python
#-*- coding:utf-8 -*-#!pip install loguru
from loguru import logger

logger 本身就是一个已经实例化好的对象,如果没有特殊的配置需求,那么自身就已经带有通用的配置参数;同时它的用法和 logging 库输出日志时的用法一致。

3、简单示例

In [1]: from loguru import logger ...:  ...: logger.debug("debug message"    ) ...: logger.info("info level message") ...: logger.warning("warning level message") ...: logger.critical("critical level message")                                                                                                                                               
2020-10-19 14:23:09.637 | DEBUG    | __main__:<module>:3 - debug message
2020-10-19 14:23:09.637 | INFO     | __main__:<module>:4 - info level message
2020-10-19 14:23:09.638 | WARNING  | __main__:<module>:5 - warning level message
2020-10-19 14:23:09.638 | CRITICAL | __main__:<module>:6 - critical level message

当你在 IDE 或终端里运行时会发现,loguru 还为输出的日志信息带上了不同的颜色样式(schema),使得结果更加美观。
在这里插入图片描述
当然,loguru 也像logging 一样为我们提供了其他可配置的部分,但相比于 logging 每次要导入特定的handler再设定一些formatter来说是更为「傻瓜化」了。

二、配置

使用基本的 add() 方法就可以对logger进行简单的配置,这些配置有点类似于使用 logging 时的handler。这里简单提及一下比较常用的几个。

1、写入日志
在不指定任何参数时,logger 默认采用 sys.stderr 标准错误输出将日志输出到控制台(console)中;但在 linux 服务器上我们有时不仅让其输出,还要以文件的形式进行留存,那么只需要在第一个参数中传入一个你想要留存文件的路径字符串即可。

#!/usr/bin/env python
#-*- coding:utf-8 -*-from loguru import logger
import oslogger.add(os.path.expanduser("~/Desktop/testlog.log"))
logger.info("hello, world!")

这样在你的桌面上就会直接出现相应的 testlog.log 日志文件了。
但是如果你没有自己要是用logging 没有预先封装来操作,那估计需要写成这样:

#!/usr/bin/env python
#-*- coding:utf-8 -*-import logging
import os
import sys
from logging import handlerslog = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)
fmt = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s")
LOGFILE = os.path.expanduser("~/Desktop/testlog.log")console_handler = logging.StreamHandler(sys.stderr)
console_handler.setFormatter(fmt)
log.addHandler(console_handler)file_handler = handlers.RotatingFileHandler(LOGFILE)
file_handler.setFormatter(fmt)
log.addHandler(file_handler)log.info("hello, world")

2、日志留存、压缩与清理
通常来说如果程序或服务的量级较大,那么就可以通过集成的日志平台或数据库来对日志信息进行存储和留存,后续有需要的话也方便进行日志分析。
但对我们个人或者一些中小型项目来说,通常只需要以文件的形式留存输出的日志即可。
尽管我们需要将日志写入到相应的文件中,如果是少量的日志那还好,但是如果是日志输出或记录时间较长的情况,那么单个日志文件就十分之大,倘若仍然是将日志都写入到一个文件中,那么当日志中的内容增长到一定数量时我们想要读取并查找相应的部分时就十分困难。这时候我们就需要对日志文件进行留存、压缩,甚至在必要时及时进行清理。
基于以上,我们可以通过对 rotationcompressionretention 三个参数进行设定来满足我们的需要:
rotation 参数能够帮助我们将日志记录以大小、时间等方式进行分割或划分:

#!/usr/bin/env python
#-*- coding:utf-8 -*-import os
from loguru import loggerLOG_DIR = os.path.expanduser("~/Desktop/logs")
LOG_FILE = os.path.join(LOG_DIR, "file_{time}.log")
if os.path.exits(LOG_DIR):os.mkdir(LOG_DIR)logger.add(LOG_FILE, rotation = "200KB")
for n in range(10000):logger.info(f"test - {n}")

2、日志留存、压缩与清理
通常来说如果程序或服务的量级较大,那么就可以通过集成的日志平台或数据库来对日志信息进行存储和留存,后续有需要的话也方便进行日志分析。
但对我们个人或者一些中小型项目来说,通常只需要以文件的形式留存输出的日志即可。
尽管我们需要将日志写入到相应的文件中,如果是少量的日志那还好,但是如果是日志输出或记录时间较长的情况,那么单个日志文件就十分之大,倘若仍然是将日志都写入到一个文件中,那么当日志中的内容增长到一定数量时我们想要读取并查找相应的部分时就十分困难。这时候我们就需要对日志文件进行留存、压缩,甚至在必要时及时进行清理。
基于以上,我们可以通过对 rotationcompressionretention 三个参数进行设定来满足我们的需要:
rotation 参数能够帮助我们将日志记录以大小、时间等方式进行分割或划分:

随着分割文件的数量越来越多之后,我们也可以进行压缩对日志进行留存,这里就要使用到 compression 参数,该参数只要你传入通用的压缩文件扩展名即可,如 ziptargz 等。

#!/usr/bin/env python
#-*- coding:utf-8 -*-import os
from loguru import loggerLOG_DIR = os.path.expanduser("~/Desktop/logs")
LOG_FILE = os.path.join(LOG_DIR, "file_{time}.log")
if os.path.exits(LOG_DIR):os.mkdir(LOG_DIR)logger.add(LOG_FILE, rotation = "200KB", compression="zip")   #压缩成zip格式
for n in range(10000):logger.info(f"test - {n}")

从结果可以看到,只要是满足了 rotation 分割后的日志文件都被直接压缩成了 zip 文件,文件大小由原本的 200kb 直接减少至 10kb,对于一些磁盘空间吃紧的 Linux 服务器来说是则是很有必要的。

当然了,如果你不想对日志进行留存,或者只想保留一段时间内的日志并对超期的日志进行删除,那么直接使用 retention 参数就好了。
这里我们可以将之前的结果随意复制 N 多份在 logs 文件夹中,然后再执行一次加上 retension 参数后代码:

#!/usr/bin/env python
#-*- coding:utf-8 -*-from loguru import loggerLOG_DIR = os.path.expanduser("~/Desktop/logs")
LOG_FILE = os.path.join(LOG_DIR, "file_{time}.log")
if not os.path.exists(LOG_DIR):os.mkdir(LOG_DIR)logger.add(LOG_FILE, rotation="200KB",retention=1)    
for n in range(10000):logger.info(f"test - {n}")

当然对 retention 传入整数时,该参数表示的是所有文件的索引,而非要保留的文件数,这里是个反直觉的小坑,用的时候注意一下就好了。所以最后我们会看到只有两个时间最近的日志文件会被保留下来,其他都被直接清理掉了。

3、序列化
如果在实际中你不太喜欢以文件的形式保留日志,那么你也可以通过 serialize 参数将其转化成序列化的json 格式,最后将导入类似于MongoDB、ElasticSearch 这类数 NoSQL 数据库中用作后续的日志分析。

#!/usr/bin/env python
#-*- coding:utf-8 -*-from loguru import logger
import oslogger.add(os.path.expanduser("~/Desktop/testlog.log"), serialize=True)
logger.info("hello, world!")

最后保存的日志都是序列化后的单条记录:

{"text": "2020-10-19 18:23:36.902 | INFO     | __main__:<module>:6 - hello, world\n","record": {"elapsed": {"repr": "0:00:00.005412","seconds": 0.005412},"exception": null,"extra": {},"file": {"name": "log_test.py","path": "/Users/Bobot/PycharmProjects/docs-python/src/loguru/log_test.py"},"function": "<module>","level": {"icon": "\u2139\ufe0f","name": "INFO","no": 20},"line": 6,"message": "hello, world","module": "log_test","name": "__main__","process": {"id": 12662,"name": "MainProcess"},"thread": {"id": 4578131392,"name": "MainThread"},"time": {"repr": "2020-10-19 18:23:36.902358+08:00","timestamp": 1602066216.902358}}
}

4、异常追溯
当异常和错误不可避免时,最好的方式就是让我们知道程序到底是哪里出了错,或者是因为什么导致错误,这样才能更好地让开发人员及时应对并解决。
loguru 集成了一个名为 better_exceptions 的库,不仅能够将异常和错误记录,并且还能对异常进行追溯,这里是来自一个官网的例子

import os
import sys
from loguru import loggerlogger.add(os.path.expanduser("~/Desktop/exception_log.log"), backtrace=True, diagnose=True)def func(a, b):return a / bdef nested(c):try:func(5, c)except ZeroDivisionError:logger.exception("What?!")if __name__ == "__main__":nested(0)

最后在日志文件中我们可以得到以下内容:

2020-10-19 21:14:11.830 | ERROR    | __main__:nested:16 - What?!
Traceback (most recent call last):File "/Users/Bobot/PycharmProjects/docs-python/src/loguru/log_test.py", line 20, in <module>nested(0)└ <function nested at 0x7fb9300c1170>> File "/Users/Bobot/PycharmProjects/docs-python/src/loguru/log_test.py", line 14, in nestedfunc(5, c)│       └ 0└ <function func at 0x7fb93010add0>File "/Users/Bobot/PycharmProjects/docs-python/src/loguru/log_test.py", line 10, in funcreturn a / b│   └ 0└ 5ZeroDivisionError: division by zero
三、与 Logging 完全兼容(Entirely Compatible)

尽管说 loguru 算是重新「造轮子」,但是它也能和logging库很好地兼容。到现在我们才谈论到 add()方法的第一个参数 sink
这个参数的英文单词动词有「下沉、浸没」等意,对于外国人来说在理解上可能没什么难的,可对我们国人来说,这可之前 logging 库中的 handler 概念还不好理解。好在前面我有说过,logurulogging 库的使用上存在相似之处,因此在后续的使用中其实我们就可以将其理解为 handler,只不过它的范围更广一些,可以除了 handler 之外的字符串、可调用方法、协程对象等。
loguru 官方文档对这一参数的解释是:

An object in charge of receiving formatted logging messages and propagating them to an appropriate endpoint.

翻译过来就是「一个用于接收格式化日志信息并将其传输合适端点的对象」,进一步形象理解就像是一个「分流器」。

#!/usr/bin/env python
#-*- coding:utf-8 -*-import logging.handlers
import os
import sysfrom loguru import loggerLOG_FILE = os.path.expanduser("~/Desktop/testlog.log")
file_handler = logging.handlers.RotatingFileHandler(LOG_FILE, encoding="utf-8")
logger.add(file_handler)
logger.debug("hello, world")

当然目前只是想在之前基于logging写好的模块中集成 loguru,只要重新编写一个继承自 logging.Handler 类并实现了 emit() 方法的 Handler即可。

#!/usr/bin/env python
#-*- coding:utf-8 -*-import logging.handlers
import os
import sysfrom loguru import loggerclass InterceptHandler(logging.Handler):def emit(self, record):try:level = logger.level(record.levelname).nameexcept ValueError:level = record.levelnoframe, depth = logging.currentframe(), 2while 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)def func(a, b):return a / bdef nested(c):try:func(5, c)except ZeroDivisionError:logging.exception("What?!")if __name__ == "__main__":nested(0)

最后结果同之前的异常追溯一致。而我们只需要在配置后直接调用logging的相关方法即可,减少了迁移和重写的成本。

这篇关于Python 日志记录库 loguru的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/293625

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

我在移动打工的日志

客户:给我搞一下录音 我:不会。不在服务范围。 客户:是不想吧 我:笑嘻嘻(气笑) 客户:小姑娘明明会,却欺负老人 我:笑嘻嘻 客户:那我交话费 我:手机号 客户:给我搞录音 我:不会。不懂。没搞过。 客户:那我交话费 我:手机号。这是电信的啊!!我这是中国移动!! 客户:我不管,我要充话费,充话费是你们的 我:可是这是移动!!中国移动!! 客户:我这是手机号 我:那又如何,这是移动!你是电信!!

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',