Python學(xué)習(xí):loguru日志庫

1.概述

python中的日志庫logging使用起來有點(diǎn)像log4j金蜀,但配置通常比較復(fù)雜沿后,構(gòu)建日志服務(wù)器時(shí)也不是方便闷游。標(biāo)準(zhǔn)庫logging的替代品是loguru惊奇,loguru使用起來就簡(jiǎn)單的多殉了。

loguru默認(rèn)的輸出格式是:時(shí)間开仰、級(jí)別、模塊、行號(hào)以及日志內(nèi)容众弓。loguru不需要手動(dòng)創(chuàng)建 logger恩溅,開箱即用,比logging使用方便得多谓娃;另外脚乡,日志輸出內(nèi)置了彩色功能,顏色和非顏色控制很方便滨达,更加友好奶稠。

loguru是非標(biāo)準(zhǔn)庫,需要事先安裝捡遍,命令是:pip3 install loguru****锌订。安裝后,最簡(jiǎn)單的使用樣例如下:

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')

上述代碼輸出:


image.png

日志打印到文件的用法也很簡(jiǎn)單画株,代碼如下:

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')

上述代碼運(yùn)行時(shí)辆飘,可以打印到console,也可以打印到文件中去谓传。

image.png

2.常見用法

2.1.顯示格式

loguru默認(rèn)格式是時(shí)間蜈项、級(jí)別、名稱+模塊和日志內(nèi)容续挟,其中名稱+模塊是寫死的紧卒,是當(dāng)前文件的__name__變量,此變量最好不要修改庸推。

工程比較復(fù)雜的情況下常侦,自定義模塊名稱,是非常有用的贬媒,容易定界定位聋亡,避免陷入細(xì)節(jié)中。我們可以通過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":表示日志格式化罪塔。<lvl>{level:8}</>表示按照日志級(jí)別顯示顏色。8表示輸出寬度為8個(gè)字符养葵。

"colorize": True:表示顯示顏色征堪。

上述代碼的輸出為:

image.png

這里寫死了模塊名稱,每個(gè)日志都這樣設(shè)置也是比較繁瑣关拒。下面會(huì)介紹指定不同模塊名稱的方法佃蚜。

2.2.寫入文件

日志一般需要持久化庸娱,除了輸出到命令行終端外,還需要寫入文件谐算。標(biāo)準(zhǔn)日志庫可以通過配置文件配置logger熟尉,在代碼中也可以實(shí)現(xiàn),但過程比較繁瑣洲脂。loguru相對(duì)而已就顯得稍微簡(jiǎn)單一些斤儿,我們看下在代碼中如何實(shí)現(xiàn)此功能。日志代碼如下:

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')

與2.1.唯一不同的地方恐锦,logger.configure新增了一個(gè)handler往果,寫入到日志文件中去。用法很簡(jiǎn)單踩蔚。

上述只是通過logger.configure設(shè)置日志格式棚放,但是模塊名不是可變的,實(shí)際項(xiàng)目開發(fā)中馅闽,不同模塊寫日志飘蚯,需要指定不同的模塊名稱。因此福也,模塊名稱需要參數(shù)化局骤,這樣實(shí)用性更強(qiáng)。樣例代碼如下:

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方法暴凑,實(shí)現(xiàn)module_name的參數(shù)化峦甩。bind返回一個(gè)日志對(duì)象,可以通過此對(duì)象進(jìn)行日志輸出现喳,這樣就可以實(shí)現(xiàn)不同模塊的日志格式凯傲。

loguru中自定義模塊名稱的功能比標(biāo)準(zhǔn)日志庫有點(diǎn)不同。通過bind方法嗦篱,可以輕松實(shí)現(xiàn)標(biāo)準(zhǔn)日志logging的功能冰单。而且,可以通過bind和logger.configure灸促,輕松實(shí)現(xiàn)結(jié)構(gòu)化日志诫欠。

上述代碼的輸出如下:


image.png

2.3.json日志

loguru保存成結(jié)構(gòu)化json格式非常簡(jiǎn)單,只需要設(shè)置serialize=True參數(shù)即可浴栽。代碼如下:

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')

輸出內(nèi)容如下:

image.png

2.4.日志繞接

loguru日志文件支持三種設(shè)置:循環(huán)荒叼、保留、壓縮典鸡。設(shè)置也比較簡(jiǎn)單被廓。尤其是壓縮格式,支持非常豐富萝玷,常見的壓縮格式都支持伊者,比如:"gz", "bz2", "xz", "lzma", "tar", "tar.gz", "tar.bz2", "tar.xz", "zip"英遭。樣例代碼如下:

from loguru import logger

logger.add("file_1.log", rotation="500 MB")  # 自動(dòng)循環(huán)過大的文件
logger.add("file_2.log", rotation="12:00")  # 每天中午創(chuàng)建新文件
logger.add("file_3.log", rotation="1 week")  # 一旦文件太舊進(jìn)行循環(huán)
logger.add("file_X.log", retention="10 days")  # 定期清理
logger.add("file_Y.log", compression="zip")  # 壓縮節(jié)省空間

2.5.并發(fā)安全

loguru默認(rèn)是線程安全的,但不是多進(jìn)程安全的亦渗,如果使用了多進(jìn)程安全,需要添加參數(shù)enqueue=True汁尺,樣例代碼如下:

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

loguru另外還支持協(xié)程法精,有興趣可以自行研究。

3.高級(jí)用法

3.1.接管標(biāo)準(zhǔn)日志logging

更換日志系統(tǒng)或者設(shè)計(jì)一套日志系統(tǒng)痴突,比較難的是兼容現(xiàn)有的代碼搂蜓,尤其是第三方庫,因?yàn)椴荒芤驗(yàn)槿罩鞠到y(tǒng)的切換辽装,而要去修改這些庫的代碼帮碰,也沒有必要。好在loguru可以方便的接管標(biāo)準(zhǔn)的日志系統(tǒng)拾积。

樣例代碼如下:

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')

# 使用標(biāo)注日志系統(tǒng)輸出
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系統(tǒng)輸出
logger.info('hello world, that is from loguru')

輸出為:

image.png

3.2.輸出日志到網(wǎng)絡(luò)服務(wù)器

如果有需要殉挽,不同進(jìn)程的日志,可以輸出到同一個(gè)日志服務(wù)器上拓巧,便于日志的統(tǒng)一管理斯碌。我們可以利用自定義或者第三方庫進(jìn)行日志服務(wù)器和客戶端的設(shè)置。下面介紹兩種日志服務(wù)器的用法肛度。

3.2.1.自定義日志服務(wù)器

日志客戶端段代碼如下:

# 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")

日志服務(wù)器代碼如下:

# 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()

運(yùn)行結(jié)果如下:

image.png

3.2.2.第三方庫日志服務(wù)器

日志客戶端代碼如下:

# 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")

日志服務(wù)器代碼如下:

# 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())

3.3.與pytest結(jié)合

官方幫助中有一個(gè)講解logurupytest結(jié)合的例子傻唾,講得有點(diǎn)含糊不是很清楚。簡(jiǎn)單的來說承耿,pytest有個(gè)fixture冠骄,可以捕捉被測(cè)方法中的logging日志打印,從而驗(yàn)證打印是否觸發(fā)加袋。

下面就詳細(xì)講述如何使用logurupytest結(jié)合的代碼凛辣,如下:

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

測(cè)試輸出如下:

image.png

附錄

1.官方幫助文檔:
https://loguru.readthedocs.io/en/stable/index.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者锁荔。
  • 序言:七十年代末蟀给,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子阳堕,更是在濱河造成了極大的恐慌跋理,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恬总,死亡現(xiàn)場(chǎng)離奇詭異前普,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)壹堰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門拭卿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來骡湖,“玉大人,你說我怎么就攤上這事峻厚∠煸蹋” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵惠桃,是天一觀的道長(zhǎng)浦夷。 經(jīng)常有香客問我,道長(zhǎng)辜王,這世上最難降的妖魔是什么劈狐? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮呐馆,結(jié)果婚禮上肥缔,老公的妹妹穿的比我還像新娘。我一直安慰自己汹来,他們只是感情好续膳,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著俗慈,像睡著了一般姑宽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上闺阱,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天炮车,我揣著相機(jī)與錄音,去河邊找鬼酣溃。 笑死瘦穆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赊豌。 我是一名探鬼主播扛或,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼碘饼!你這毒婦竟也來了熙兔?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤艾恼,失蹤者是張志新(化名)和其女友劉穎住涉,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钠绍,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舆声,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片媳握。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碱屁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛾找,到底是詐尸還是另有隱情娩脾,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布打毛,位于F島的核電站晦雨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏隘冲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一绑雄、第九天 我趴在偏房一處隱蔽的房頂上張望展辞。 院中可真熱鬧,春花似錦万牺、人聲如沸罗珍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽覆旱。三九已至,卻和暖如春核无,著一層夾襖步出監(jiān)牢的瞬間扣唱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工团南, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留噪沙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓吐根,卻偏偏與公主長(zhǎng)得像正歼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拷橘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容