python
logging
agak seperti log4j
untuk digunakan, tetapi konfigurasi biasanya lebih rumit dan tidak mudah untuk membina pelayan log. Penggantian pustaka standard logging
ialah loguru
, yang lebih mudah digunakan. loguru
Format output lalai ialah: masa, tahap, modul, nombor baris dan kandungan log. loguru
Tidak perlu membuat loguru
secara manual, ia boleh digunakan di luar kotak, yang lebih mudah digunakan daripada logger
selain itu, output log mempunyai fungsi warna dan warna terbina dalam dan kawalan bukan warna adalah sangat mudah dan lebih mesra pengguna. logging
ialah perpustakaan bukan standard dan perlu dipasang terlebih dahulu Perintahnya ialah: **loguru
****. **Selepas pemasangan, contoh penggunaan paling mudah adalah seperti berikut: pip3 install loguru
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')
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')
Format lalai ialah masa, tahap, nama + modul dan log Kandungan, di mana nama + modul berkod keras, ialah pembolehubah loguru
bagi fail semasa Sebaiknya jangan ubah suai pembolehubah ini. __name__
. Seperti berikut: 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')
Output kod di atas ialah:: menunjukkan pemegang keluaran log atau destinasi,
handlers
menunjukkan output ke terminal baris arahan.sys.stderr
bermaksud output ke terminal
"sink": sys.stderr
bermaksud pemformatan log.
"format":
bermaksud memaparkan warna mengikut tahap log. 8 bermakna lebar output ialah 8 aksara.<lvl>{level:8}</>
"colorize":
**: Menunjukkan warna paparan.True
Nama modul dikodkan keras di sini dan agak menyusahkan untuk sediakan setiap log dengan cara ini. Berikut menerangkan cara menentukan nama modul yang berbeza.2.2. Menulis fail Log secara amnya perlu diteruskan sebagai tambahan kepada terminal baris arahan, ia juga perlu ditulis ke fail. Pustaka log standard boleh mengkonfigurasi logger melalui fail konfigurasi, dan ia juga boleh dilaksanakan dalam kod, tetapi prosesnya agak rumit. Loguru adalah agak mudah Mari kita lihat bagaimana untuk melaksanakan fungsi ini dalam kod. Kod log adalah seperti berikut:
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')
Satu-satunya perbezaan daripada 2.1 ialahDi atas hanya menetapkan format log melaluimenambah
logger.configure
baharu dan menulisnya pada fail log. Penggunaannya sangat mudah.handler
, tetapi nama modul tidak berubah Dalam pembangunan projek sebenar, modul yang berbeza perlu menentukan nama modul yang berbeza semasa menulis log. Oleh itu, nama modul perlu diparameterkan untuk menjadikannya lebih praktikal. Kod sampel adalah seperti berikut: 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")
Output kod di atas adalah seperti berikut: 2.3.json logParameterisasi
Fungsi menyesuaikan nama modul dalam loguru sedikit berbeza daripada perpustakaan pengelogan standard. Melalui kaedah bind, fungsi log standardlogger.bind(module_name='my-loguru')
dilaksanakan melalui kaedah bind. bind mengembalikan objek log di mana output log boleh dilakukan, supaya format log untuk modul berbeza boleh dilaksanakan.module_name
boleh dilaksanakan dengan mudah. Selain itu, log berstruktur boleh dilaksanakan dengan mudah melalui
logging
.bind和logger.configure
Simpan dalam format json berstruktur Mudah, cuma tetapkan parameter loguru
. Kodnya adalah seperti berikut: 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')
Fail log menyokong tiga tetapan: Gelung, simpan, mampat. Persediaan juga agak mudah. Terutamanya format mampatan sangat kaya dengan sokongan Format mampatan biasa disokong, seperti: loguru
, "gz"
, "bz2"
, "xz"
, "lzma"
, "tar"
, "tar.gz"
, "tar.bz2"
, "tar.xz"
. Kod sampel adalah seperti berikut: "zip"
from loguru import logger logger.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") # 压缩节省空间
loguru
默认是线程安全的,但不是多进程安全的,如果使用了多进程安全,需要添加参数enqueue=True
,样例代码如下:
logger.add("somefile.log", enqueue=True)
loguru
另外还支持协程,有兴趣可以自行研究。
更换日志系统或者设计一套日志系统,比较难的是兼容现有的代码,尤其是第三方库,因为不能因为日志系统的切换,而要去修改这些库的代码,也没有必要。好在loguru
可以方便的接管标准的日志系统。
样例代码如下:
import logging import logging.handlers import sys from loguru import logger handler = logging.handlers.SysLogHandler(address=('localhost', 514)) logger.add(handler) class LoguruHandler(logging.Handler): def emit(self, record): try: level = logger.level(record.levelname).name except ValueError: level = record.levelno frame, depth = logging.currentframe(), 2 while frame.f_code.co_filename == logging.__file__: frame = frame.f_back depth += 1 logger.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')
输出为:
如果有需要,不同进程的日志,可以输出到同一个日志服务器上,便于日志的统一管理。我们可以利用自定义或者第三方库进行日志服务器和客户端的设置。下面介绍两种日志服务器的用法。
3.2.1.自定义日志服务器
日志客户端段代码如下:
# client.py import pickle import socket import struct import time from loguru import logger class 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.record data = 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 struct from loguru import logger class LoggingStreamHandler(socketserver.StreamRequestHandler): def handle(self): while True: chunk = self.connection.recv(4) if len(chunk) < 4: break slen = 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()
运行结果如下:
3.2.2.第三方库日志服务器
日志客户端代码如下:
# client.py import zmq from zmq.log.handlers import PUBHandler from loguru import logger socket = 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 logger socket = 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())
官方帮助中有一个讲解loguru
与pytest
结合的例子,讲得有点含糊不是很清楚。简单的来说,pytest
有个fixture
,可以捕捉被测方法中的logging
日志打印,从而验证打印是否触发。
下面就详细讲述如何使用loguru
与pytest
结合的代码,如下:
import pytest from _pytest.logging import LogCaptureFixture from loguru import logger def 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 caplog logger.remove(handler_id) def test_some_func_logs_warning(caplog): assert some_func(-1, 3) == 2 assert "Oh no!" in caplog.text
测试输出如下:
Atas ialah kandungan terperinci Cara menggunakan perpustakaan log loguru dalam Python. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!