文章目录
一. 概述
在 Python 中用到日志记录,那就不可避免地会用到内置的 logging标准库 。虽然logging 库采用的是模块化设计,你可以设置不同的 handler 来进行组合,但是在配置上通常较为繁琐;而且如果不是特别处理,在一些多线程或多进程的场景下使用 logging还会导致日志记录会出现错乱或是丢失的情况。
关于Python内置的 logging标准库介绍可参见博客:Python 日志记录工具logging
标准库logging的替代品是loguru,loguru使用起来就简单的多。它不仅能够减少繁琐的配置过程还能实现和logging类似的功能,同时还能保证日志记录的线程进程安全,又能够和logging 相兼容,并进一步追踪异常也能进行代码回溯。
loguru默认的输出格式是:时间、级别、模块、行号以及日志内容。loguru不需要手动创建 logger,开箱即用;另外,日志输出内置了彩色功能,颜色和非颜色控制很方便,更加友好。
loguru官方文档:https://loguru.readthedocs.io/en/stable/index.html
二、基本使用
2.1 安装
loguru是非标准库,需要事先安装,命令是:
pip install loguru
logger 本身就是一个已经实例化好的对象,如果没有特殊的配置需求,那么自身就已经带有通用的配置参数;同时它的用法和 logging库输出日志时的用法一致。
2.2 日志输出
(1)输出到控制台
安装后,最简单的使用样例如下:
from loguru import logger logger.debug('hello, this debug loguru') logger.info('hello, this is info loguru') logger.warning('hello, this is warning loguru') logger.error('hello, this is error loguru') logger.critical('hello, this is critical loguru')
上述代码输出:
(2)输出到控制台+文件
日志打印到文件的用法也很简单,代码如下:
from loguru import logger logger.add('myloguru.log') logger.debug('hello, this debug loguru') logger.info('hello, this is info loguru') logger.warning('hello, this is warning loguru') logger.error('hello, this is error loguru') logger.critical('hello, this is critical loguru')
上述代码运行时,可以打印到console,也可以打印到文件中去。
三、进阶用法
3.1 显示格式
loguru默认格式是时间、级别、名称+模块和日志内容,其中名称+模块是写死的,是当前文件的__name__变量,此变量最好不要修改。
工程比较复杂的情况下,自定义模块名称,是非常有用的,容易定界定位,避免陷入细节中。我们可以通过logger.configure手工指定模块名称。如下如下:
import sys from loguru import logger logger.configure(handlers=[ { "sink": sys.stderr, "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>mymodule</> | - <lvl>{message}</>", "colorize": True }, ]) logger.debug('this is debug') logger.info('this is info') logger.warning('this is warning') logger.error('this is error') logger.critical('this is critical')
上述代码中:
- handlers:表示日志输出句柄或者目的地,sys.stderr表示输出到命令行终端。
- “sink”: sys.stderr,表示输出到终端
- “format”:表示日志格式化。{level:8}</>表示按照日志级别显示颜色。8表示输出宽度为8个字符。
- “colorize”: True**:表示显示颜色。
上述代码的输出为:
这里写死了模块名称,每个日志都这样设置也是比较繁琐。下面会介绍指定不同模块名称的方法。
3.2 写入文件
日志一般需要持久化,除了输出到命令行终端外,还需要写入文件。标准日志库可以通过配置文件配置logger,在代码中也可以实现,但过程比较繁琐。loguru相对而已就显得稍微简单一些,我们看下在代码中如何实现此功能。日志代码如下:
import sys from loguru import logger logger.configure(handlers=[ { "sink": sys.stderr, "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>mymodule</> | - <lvl>{message}</>", "colorize": True }, { "sink": 'first.log', "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |{level:8}| {name} : {module}:{line:4} | mymodule | - {message}", "colorize": False }, ]) logger.debug('this is debug') logger.info('this is info') logger.warning('this is warning') logger.error('this is error') logger.critical('this is critical')
与3.1 唯一不同的地方,logger.configure新增了一个handler,写入到日志文件中去。用法很简单。
3.3 模块名参数化
上述只是通过logger.configure设置日志格式,但是模块名不是可变的,实际项目开发中,不同模块写日志,需要指定不同的模块名称。因此,模块名称需要参数化,这样实用性更强。样例代码如下:
import sys from loguru import logger logger.configure(handlers=[ { "sink": sys.stderr, "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | <cyan>{extra[module_name]}</> | - <lvl>{message}</>", "colorize": True }, { "sink": 'first.log', "format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |{level:8}| {name} : {module}:{line:4} | {extra[module_name]} | - {message}", "colorize": False }, ]) log = logger.bind(module_name='my-loguru') log.debug("this is hello, module is my-loguru") log2 = logger.bind(module_name='my-loguru2') log2.info("this is hello, module is my-loguru2")
logger.bind(module_name=‘my-loguru’)通过bind方法,实现module_name的参数化。bind返回一个日志对象,可以通过此对象进行日志输出,这样就可以实现不同模块的日志格式。
loguru中自定义模块名称的功能比标准日志库有点不同。通过bind方法,可以轻松实现标准日志logging的功能。而且,可以通过bind和logger.configure,轻松实现结构化日志。
上述代码的输出如下:
3.4 日志留存、压缩与清理
通常来说如果程序或服务的量级较大,那么就可以通过集成的日志平台或数据库来对日志信息进行存储和留存,后续有需要的话也方便进行日志分析。
但对我们个人或者一些中小型项目来说,通常只需要以文件的形式留存输出的日志即可。
尽管我们需要将日志写入到相应的文件中,如果是少量的日志那还好,但是如果是日志输出或记录时间较长的情况,那么单个日志文件就十分之大,倘若仍然是将日志都写入到一个文件中,那么当日志中的内容增长到一定数量时我们想要读取并查找相应的部分时就十分困难。这时候我们就需要对日志文件进行留存、压缩,甚至在必要时及时进行清理。
基于以上,我们可以通过对rotation
、compression
和retention
三个参数进行设定来满足我们的需要:
- rotation :rotation参数能够帮助我们将日志记录以文件大小、时间等方式进行分割或划分。
# 设置每天 0 点新创建一个 log 文件: logger.add('runtime_{time}.log', rotation='00:00') # 设置超过 500 MB 新创建一个 log 文件: logger.add('runtime_{time}.log', rotation="500 MB") # 设置每隔一个周新创建一个 log 文件: logger.add('runtime_{time}.log', rotation='1 week')
- compression :随着分割文件的数量越来越多之后,我们也可以进行压缩对日志进行留存,这里就要使用到 compression参数,该参数只要你传入通用的压缩文件扩展名即可,如zip、tar、gz等。
# 设置使用 zip 文件格式保存 logger.add('runtime_{time}.log', compression='zip')
- retention :如果你不想对日志进行留存,或者只想保留一段时间内的日志并对超期的日志进行删除,那么可以使用 retention 参数。
# 设置日志文件最长保留 15 天: logger.add('runtime_{time}.log', retention='15 days') # 设置日志文件最多保留 10 个: logger.add('runtime_{time}.log', retention=10) # retention的参数也可以是一个 datetime.timedelta 对象,比如设置日志文件最多保留 5 个小时: import datetime logger.add('runtime_{time}.log', retention=datetime.timedelta(hours=5))
3.5 序列化为json格式
如果在实际中你不太喜欢以文件的形式保留日志,那么你也可以通过 serialize 参数将其转化成序列化的json格式,最后将导入类似于MongoDB、ElasticSearch 这类数NoSQL 数据库中用作后续的日志分析。
loguru保存成结构化json格式非常简单,只需要设置serialize=True参数即可。代码如下:
from loguru import logger logger.add('json.log', serialize=True, encoding='utf-8') logger.debug('this is debug message') logger.info('this is info message') logger.error('this is error message')
输出内容如下:
3.6 并发安全
loguru默认是线程安全的,但不是多进程安全的,如果使用了多进程安全,需要添加参数enqueue=True,样例代码如下:
logger.add("somefile.log", enqueue=True)