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

2023-12-04 05:04

本文主要是介绍【拓展】Loguru:更为优雅、简洁的Python 日志管理模块,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、简单介绍

二、安装与简单使用

​三、常见用法

3.1 显示格式

3.2 写入文件

3.3 json日志

3.4 日志绕接

3.5 并发安全

四、高级用法

4.1 接管标准日志logging

4.2 输出日志到网络服务器

4.2.1 自定义日志服务器

​4.2.2 第三方库日志服务器

4.3 与pytest结合


一、简单介绍

python中的日志库logging使用起来有点像log4j,但配置通常比较复杂,构建日志服务器时也不是方便。标准库logging的替代品是loguru,loguru使用起来就简单的多。

loguru默认的输出格式是:时间、级别、模块、行号以及日志内容。loguru不需要手动创建 logger,开箱即用,比logging使用方便得多;另外,日志输出内置了彩色功能,颜色和非颜色控制很方便,更加友好。

二、安装与简单使用

  • 使用 pip 安装即可,Python 3 版本的安装如下:
pip3 install loguru
  • 简单使用
    • 我们直接通过导入loguru 封装好的logger 类的实例化对象,不需要手动创建 logger,直接进行调用不同级别的日志输出方法。
from loguru import loggerlogger.debug('This is debug information')
logger.info('This is info information')
logger.warning('This is warn information')
logger.error('This is error information')

上述代码输出: 

 日志打印到文件的用法也很简单,代码如下:

from loguru import loggerlogger.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 sysfrom loguru import loggerlogger.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":表示日志格式化。<lvl>{level:8}</>表示按照日志级别显示颜色。8表示输出宽度为8个字符。

"colorize": True**:表示显示颜色。

上述代码的输出为:

 这里写死了模块名称,每个日志都这样设置也是比较繁琐。下面会介绍指定不同模块名称的方法。

3.2 写入文件

loguru默认格式是时间、级别、名称+模块和日志内容,其中名称+模块是写死的,是当前文件的__name__变量,此变量最好不要修改。

工程比较复杂的情况下,自定义模块名称,是非常有用的,容易定界定位,避免陷入细节中。我们可以通过logger.configure手工指定模块名称。如下:

import sysfrom loguru import loggerlogger.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,写入到日志文件中去。用法很简单。

上述只是通过logger.configure设置日志格式,但是模块名不是可变的,实际项目开发中,不同模块写日志,需要指定不同的模块名称。因此,模块名称需要参数化,这样实用性更强。样例代码如下:

import sysfrom loguru import loggerlogger.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.3 json日志

loguru保存成结构化json格式非常简单,只需要设置serialize=True参数即可。代码如下:

from loguru import loggerlogger.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.4 日志绕接

loguru日志文件支持三种设置:循环、保留、压缩。设置也比较简单。尤其是压缩格式,支持非常丰富,常见的压缩格式都支持,比如:"gz", "bz2", "xz", "lzma", "tar", "tar.gz", "tar.bz2", "tar.xz", "zip"。样例代码如下:

from loguru import loggerlogger.add("file_1.log", rotation="500 MB")  # 自动循环过大的文件
logger.add("file_2.log", rotation="12:00")  # 每天中午创建新文件
logger.add("file_3.log", rotation="1 week")  # 一旦文件太旧进行循环
logger.add("file_X.log", retention="10 days")  # 定期清理
logger.add("file_Y.log", compression="zip")  # 压缩节省空间

3.5 并发安全

loguru默认是线程安全的,但不是多进程安全的,如果使用了多进程安全,需要添加参数enqueue=True,样例代码如下:

logger.add("somefile.log", enqueue=True)

四、高级用法

4.1 接管标准日志logging

更换日志系统或者设计一套日志系统,比较难的是兼容现有的代码,尤其是第三方库,因为不能因为日志系统的切换,而要去修改这些库的代码,也没有必要。好在loguru可以方便的接管标准的日志系统。

样例代码如下:

import logging
import logging.handlers
import sysfrom loguru import loggerhandler = logging.handlers.SysLogHandler(address=('localhost', 514))
logger.add(handler)class LoguruHandler(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=[LoguruHandler()], level=0, format='%(asctime)s %(filename)s %(levelname)s %(message)s',datefmt='%Y-%M-%D %H:%M:%S')logger.configure(handlers=[{"sink": sys.stderr,"format": "{time:YYYY-MM-DD HH:mm:ss.SSS} |<lvl>{level:8}</>| {name} : {module}:{line:4} | [ModuleA] | - <lvl>{message}</>","colorize": True},
])log = logging.getLogger('root')# 使用标注日志系统输出
log.info('hello wrold, that is from logging')
log.debug('debug hello world, that is from logging')
log.error('error hello world, that is from logging')
log.warning('warning hello world, that is from logging')# 使用loguru系统输出
logger.info('hello world, that is from loguru')

输出为:

4.2 输出日志到网络服务器

如果有需要,不同进程的日志,可以输出到同一个日志服务器上,便于日志的统一管理。我们可以利用自定义或者第三方库进行日志服务器和客户端的设置。下面介绍两种日志服务器的用法。

4.2.1 自定义日志服务器

日志客户端段代码如下:

# client.py
import pickle
import socket
import struct
import timefrom loguru import loggerclass SocketHandler:def __init__(self, host, port):self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.sock.connect((host, port))def write(self, message):record = message.recorddata = pickle.dumps(record)slen = struct.pack(">L", len(data))self.sock.send(slen + data)logger.configure(handlers=[{"sink": SocketHandler('localhost', 9999)}])while True:time.sleep(1)logger.info("Sending info message from the client")logger.debug("Sending debug message from the client")logger.error("Sending error message from the client")

日志服务器代码如下:

# server.py
import pickle
import socketserver
import structfrom loguru import loggerclass LoggingStreamHandler(socketserver.StreamRequestHandler):def handle(self):while True:chunk = self.connection.recv(4)if len(chunk) < 4:breakslen = struct.unpack('>L', chunk)[0]chunk = self.connection.recv(slen)while len(chunk) < slen:chunk = chunk + self.connection.recv(slen - len(chunk))record = pickle.loads(chunk)level, message = record["level"].no, record["message"]logger.patch(lambda record: record.update(record)).log(level, message)server = socketserver.TCPServer(('localhost', 9999), LoggingStreamHandler)
server.serve_forever()

运行结果如下:

4.2.2 第三方库日志服务器

日志客户端代码如下:

# client.py
import zmq
from zmq.log.handlers import PUBHandler
from loguru import loggersocket = zmq.Context().socket(zmq.PUB)
socket.connect("tcp://127.0.0.1:12345")
handler = PUBHandler(socket)logger.add(handler)
logger.info("Logging from client")

日志服务器代码如下:

# server.py
import sys
import zmq
from loguru import loggersocket = zmq.Context().socket(zmq.SUB)
socket.bind("tcp://127.0.0.1:12345")
socket.subscribe("")
logger.configure(handlers=[{"sink": sys.stderr, "format": "{message}"}])while True:_, message = socket.recv_multipart()logger.info(message.decode("utf8").strip())

4.3 与pytest结合

官方帮助中有一个讲解loguru与pytest结合的例子,讲得有点含糊不是很清楚。简单的来说,pytest有个fixture,可以捕捉被测方法中的logging日志打印,从而验证打印是否触发。

下面就详细讲述如何使用loguru与pytest结合的代码,如下:

import pytest
from _pytest.logging import LogCaptureFixture
from loguru import loggerdef some_func(i, j):logger.info('Oh no!')logger.info('haha')return i + j@pytest.fixture
def caplog(caplog: LogCaptureFixture):handler_id = logger.add(caplog.handler, format="{message}")yield caploglogger.remove(handler_id)def test_some_func_logs_warning(caplog):assert some_func(-1, 3) == 2assert "Oh no!" in caplog.text

测试输出如下:

这篇关于【拓展】Loguru:更为优雅、简洁的Python 日志管理模块的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

使用Python绘制蛇年春节祝福艺术图

《使用Python绘制蛇年春节祝福艺术图》:本文主要介绍如何使用Python的Matplotlib库绘制一幅富有创意的“蛇年有福”艺术图,这幅图结合了数字,蛇形,花朵等装饰,需要的可以参考下... 目录1. 绘图的基本概念2. 准备工作3. 实现代码解析3.1 设置绘图画布3.2 绘制数字“2025”3.3

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

python 字典d[k]中key不存在的解决方案

《python字典d[k]中key不存在的解决方案》本文主要介绍了在Python中处理字典键不存在时获取默认值的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录defaultdict:处理找不到的键的一个选择特殊方法__missing__有时候为了方便起见,

使用Python绘制可爱的招财猫

《使用Python绘制可爱的招财猫》招财猫,也被称为“幸运猫”,是一种象征财富和好运的吉祥物,经常出现在亚洲文化的商店、餐厅和家庭中,今天,我将带你用Python和matplotlib库从零开始绘制一... 目录1. 为什么选择用 python 绘制?2. 绘图的基本概念3. 实现代码解析3.1 设置绘图画

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一