別再手動(dòng)配置logging了大家都在用loguru

在部署一些定時(shí)運(yùn)行或者長(zhǎng)期運(yùn)行的任務(wù)時(shí)走芋,為了留存一些導(dǎo)致程序出現(xiàn)異常或錯(cuò)誤的信息潘鲫,通常會(huì)才用日志的方式來(lái)進(jìn)行記錄這些信息翁逞。

Python 中用到日志記錄,那就不可避免地會(huì)用到內(nèi)置的 logging標(biāo)準(zhǔn)庫(kù) 溉仑。雖然logging 庫(kù)采用的是模塊化設(shè)計(jì)挖函,你可以設(shè)置不同的 handler 來(lái)進(jìn)行組合,但是在配置上通常較為繁瑣浊竟;而且如果不是特別處理怨喘,在一些多線程或多進(jìn)程的場(chǎng)景下使用 logging還會(huì)導(dǎo)致日志記錄會(huì)出現(xiàn)錯(cuò)亂或是丟失的情況。

但有這么一個(gè)庫(kù)振定,它不僅能夠減少繁瑣的配置過(guò)程還能實(shí)現(xiàn)和logging類(lèi)似的功能必怜,同時(shí)還能保證日志記錄的線程進(jìn)程安全,又能夠和logging 相兼容后频,并進(jìn)一步追蹤異常也能進(jìn)行代碼回溯梳庆。這個(gè)庫(kù)叫loguru——一個(gè)專為像我這樣懶人而生日志記錄庫(kù)。

loguru 庫(kù)的使用可以說(shuō)是十分簡(jiǎn)單卑惜,我們直接可以通過(guò)導(dǎo)入它本身封裝好的logger 類(lèi)就可以直接進(jìn)行調(diào)用膏执。

#!pip install loguru
from loguru import logger

logger 本身就是一個(gè)已經(jīng)實(shí)例化好的對(duì)象,如果沒(méi)有特殊的配置需求露久,那么自身就已經(jīng)帶有通用的配置參數(shù)胧后;同時(shí)它的用法和 logging庫(kù)輸出日志時(shí)的用法一致

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-07 14:23:09.637 | DEBUG    | __main__:<module>:3 - debug message
2020-10-07 14:23:09.637 | INFO     | __main__:<module>:4 - info level message
2020-10-07 14:23:09.638 | WARNING  | __main__:<module>:5 - warning level message
2020-10-07 14:23:09.638 | CRITICAL | __main__:<module>:6 - critical level message

當(dāng)你在IDE 或終端里運(yùn)行時(shí)會(huì)發(fā)現(xiàn),loguru 還為輸出的日志信息帶上了不同的顏色樣式(schema)抱环,使得結(jié)果更加美觀壳快。

1.png

當(dāng)然纸巷,loguru 也像logging一樣為我們提供了其他可配置的部分,但相比于 logging 每次要導(dǎo)入特定的handler再設(shè)定一些formatter來(lái)說(shuō)是更為「傻瓜化」了眶痰。

配置

使用基本的add() 方法就可以對(duì)logger 進(jìn)行簡(jiǎn)單的配置瘤旨,這些配置有點(diǎn)類(lèi)似于使用 logging 時(shí)的 handler。這里簡(jiǎn)單提及一下比較常用的幾個(gè)竖伯。

寫(xiě)入日志

在不指定任何參數(shù)時(shí)存哲,logger 默認(rèn)采用 sys.stderr 標(biāo)準(zhǔn)錯(cuò)誤輸出將日志輸出到控制臺(tái)(console)中;但在linux 服務(wù)器上我們有時(shí)不僅讓其輸出七婴,還要以文件的形式進(jìn)行留存祟偷,那么只需要在第一個(gè)參數(shù)中傳入一個(gè)你想要留存文件的路徑字符串即可。就像這樣:

from loguru import logger
import os

logger.add(os.path.expanduser("~/Desktop/testlog.log"))
logger.info("hello, world!")

這樣在你的桌面上就會(huì)直接出現(xiàn)相應(yīng)的testlog.log日志文件了打厘。

但是如果你沒(méi)有自己要是用logging沒(méi)有預(yù)先封裝來(lái)操作修肠,那估計(jì)你得寫(xiě)成這樣:

import logging
import os
import sys
from logging import handlers

log = 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")

碼字不易廢話兩句:有需要python學(xué)習(xí)資料的或者有技術(shù)問(wèn)題交流點(diǎn)擊下方鏈接即可
https://docs.qq.com/doc/DTGpFa2lVeE9jUkRv

日志留存、壓縮與清理

通常來(lái)說(shuō)如果程序或服務(wù)的量級(jí)較大户盯,那么就可以通過(guò)集成的日志平臺(tái)或數(shù)據(jù)庫(kù)來(lái)對(duì)日志信息進(jìn)行存儲(chǔ)和留存嵌施,后續(xù)有需要的話也方便進(jìn)行日志分析。

但對(duì)我們個(gè)人或者一些中小型項(xiàng)目來(lái)說(shuō)莽鸭,通常只需要以文件的形式留存輸出的日志即可吗伤。

盡管我們需要將日志寫(xiě)入到相應(yīng)的文件中,如果是少量的日志那還好硫眨,但是如果是日志輸出或記錄時(shí)間較長(zhǎng)的情況足淆,那么單個(gè)日志文件就十分之大,倘若仍然是將日志都寫(xiě)入到一個(gè)文件中礁阁,那么當(dāng)日志中的內(nèi)容增長(zhǎng)到一定數(shù)量時(shí)我們想要讀取并查找相應(yīng)的部分時(shí)就十分困難缸浦。這時(shí)候我們就需要對(duì)日志文件進(jìn)行留存、壓縮氮兵,甚至在必要時(shí)及時(shí)進(jìn)行清理裂逐。

基于以上,我們可以通過(guò)對(duì)rotation 泣栈、compressionretention 三個(gè)參數(shù)進(jìn)行設(shè)定來(lái)滿足我們的需要:

rotation 參數(shù)能夠幫助我們將日志記錄以大小卜高、時(shí)間等方式進(jìn)行分割或劃分:

mport os
from loguru import logger

LOG_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}")

最后呈現(xiàn)如下:

2.png

隨著分割文件的數(shù)量越來(lái)越多之后,我們也可以進(jìn)行壓縮對(duì)日志進(jìn)行留存南片,這里就要使用到 compression參數(shù)掺涛,該參數(shù)只要你傳入通用的壓縮文件擴(kuò)展名即可,如zip疼进、tar薪缆、gz等。

import os
from loguru import logger

LOG_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")
for n in range(10000):
    logger.info(f"test - {n}")

從結(jié)果可以看到伞广,只要是滿足了rotation分割后的日志文件都被直接壓縮成了zip 文件拣帽,文件大小由原本的 200kb 直接減少至10kb疼电,對(duì)于一些磁盤(pán)空間吃緊的Linux服務(wù)器來(lái)說(shuō)是則是很有必要的。

3.png

當(dāng)然了减拭,如果你不想對(duì)日志進(jìn)行留存蔽豺,或者只想保留一段時(shí)間內(nèi)的日志并對(duì)超期的日志進(jìn)行刪除,那么直接使用 retention 參數(shù)就好了拧粪。

這里我們可以將之前的結(jié)果隨意復(fù)制 N 多份在logs文件夾中修陡,然后再執(zhí)行一次加上 retension 參數(shù)后代碼:

from loguru import logger

LOG_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}")

當(dāng)然對(duì)retention 傳入整數(shù)時(shí),該參數(shù)表示的是所有文件的索引可霎,而非要保留的文件數(shù)魄鸦,這里是個(gè)反直覺(jué)的小坑,用的時(shí)候注意一下就好了癣朗。所以最后我們會(huì)看到只有兩個(gè)時(shí)間最近的日志文件會(huì)被保留下來(lái)拾因,其他都被直接清理掉了。

4.png

序列化

如果在實(shí)際中你不太喜歡以文件的形式保留日志斯棒,那么你也可以通過(guò) serialize 參數(shù)將其轉(zhuǎn)化成序列化的json格式盾致,最后將導(dǎo)入類(lèi)似于MongoDB主经、ElasticSearch 這類(lèi)數(shù)NoSQL 數(shù)據(jù)庫(kù)中用作后續(xù)的日志分析荣暮。

from loguru import logger
import os

logger.add(os.path.expanduser("~/Desktop/testlog.log"), serialize=True)
logger.info("hello, world!")

最后保存的日志都是序列化后的單條記錄:

{
    "text": "2020-10-07 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-07 18:23:36.902358+08:00",
            "timestamp": 1602066216.902358
        }
    }
}

異常追溯

當(dāng)異常和錯(cuò)誤不可避免時(shí),最好的方式就是讓我們知道程序到底是哪里出了錯(cuò)罩驻,或者是因?yàn)槭裁磳?dǎo)致錯(cuò)誤穗酥,這樣才能更好地讓開(kāi)發(fā)人員及時(shí)應(yīng)對(duì)并解決。

loguru集成了一個(gè)名為better_exceptions 的庫(kù)惠遏,不僅能夠?qū)惓:湾e(cuò)誤記錄砾跃,并且還能對(duì)異常進(jìn)行追溯,這里是來(lái)自一個(gè)官網(wǎng)的例子

import os
import sys

from loguru import logger

logger.add(os.path.expanduser("~/Desktop/exception_log.log"), backtrace=True, diagnose=True)

def func(a, b):
    return a / b

def nested(c):
    try:
        func(5, c)
    except ZeroDivisionError:
        logger.exception("What?!")

if __name__ == "__main__":
    nested(0)

最后在日志文件中我們可以得到以下內(nèi)容:

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 nested
    func(5, c)
    │       └ 0
    └ <function func at 0x7fb93010add0>

  File "/Users/Bobot/PycharmProjects/docs-python/src/loguru/log_test.py", line 10, in func
    return a / b
           │   └ 0
           └ 5

ZeroDivisionError: division by zero

與 Logging 完全兼容(Entirely Compatible)

盡管說(shuō)loguru 算是重新「造輪子」节吮,但是它也能和logging庫(kù)很好地兼容抽高。到現(xiàn)在我們才談?wù)摰?code>add() 方法的第一個(gè)參數(shù) sink

這個(gè)參數(shù)的英文單詞動(dòng)詞有「下沉透绩、浸沒(méi)」等意翘骂,對(duì)于外國(guó)人來(lái)說(shuō)在理解上可能沒(méi)什么難的,可對(duì)我們國(guó)人來(lái)說(shuō)帚豪,這可之前logging 庫(kù)中的handler 概念還不好理解碳竟。好在前面我有說(shuō)過(guò),logurulogging 庫(kù)的使用上存在相似之處狸臣,因此在后續(xù)的使用中其實(shí)我們就可以將其理解為handler莹桅,只不過(guò)它的范圍更廣一些,可以除了 handler 之外的字符串烛亦、可調(diào)用方法诈泼、協(xié)程對(duì)象等懂拾。

loguru 官方文檔對(duì)這一參數(shù)的解釋是:

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

翻譯過(guò)來(lái)就是「一個(gè)用于接收格式化日志信息并將其傳輸合適端點(diǎn)的對(duì)象」,進(jìn)一步形象理解就像是一個(gè)「分流器」厂汗。

import logging.handlers
import os
import sys

from loguru import logger

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

當(dāng)然目前只是想在之前基于logging 寫(xiě)好的模塊中集成loguru委粉,只要重新編寫(xiě)一個(gè)繼承自 logging.Handler 類(lèi)并實(shí)現(xiàn)了emit() 方法的Handler 即可。

import logging.handlers
import os
import sys

from loguru import logger

class InterceptHandler(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=[InterceptHandler()], level=0)

def func(a, b):
    return a / b

def nested(c):
    try:
        func(5, c)
    except ZeroDivisionError:
        logging.exception("What?!")

if __name__ == "__main__":
    nested(0)

后結(jié)果同之前的異常追溯一致娶桦。而我們只需要在配置后直接調(diào)用logging 的相關(guān)方法即可贾节,減少了遷移和重寫(xiě)的成本。

最后

本文介紹了關(guān)于loguru的常用方法衷畦,從對(duì)比例子上來(lái)看栗涂,相比于復(fù)雜的 logging 配置來(lái)說(shuō),使用loguru 庫(kù)無(wú)疑還是很香的祈争,畢竟別人已經(jīng)為我們一些日常的通用性需求提供了封裝好的解決方案斤程,無(wú)論是在學(xué)習(xí)還是在使用的成本上,無(wú)疑還是比較小的菩混。

由于篇幅有限忿墅,loguru的其他配置部分沒(méi)有進(jìn)一步展開(kāi),如果看完本文的你對(duì)這個(gè)庫(kù)感興趣并打算投入到實(shí)際的開(kāi)發(fā)和生產(chǎn)中使用沮峡,那么建議你還是閱讀一下其官方文檔疚脐,有必要的話可以瀏覽一下源碼。

不過(guò)loguru的通用配置不一定滿足每個(gè)人的需要邢疙,對(duì)于那些動(dòng)手能力強(qiáng)或水平較高的朋友還能進(jìn)一步根據(jù)個(gè)人需求或業(yè)務(wù)需求進(jìn)行二次封裝棍弄,或許也能較為貼合實(shí)際情況。

以上就是小編今天為大家?guī)?lái)的內(nèi)容疟游,小編本身就是一名python開(kāi)發(fā)工程師呼畸,我自己花了三天時(shí)間整理了一套python學(xué)習(xí)教程,從最基礎(chǔ)的python腳本到web開(kāi)發(fā)颁虐,爬蟲(chóng)蛮原,數(shù)據(jù)分析,數(shù)據(jù)可視化另绩,機(jī)器學(xué)習(xí)儒陨,等,這些資料有想要的小伙伴點(diǎn)擊下方連接即可領(lǐng)取
https://docs.qq.com/doc/DTGpFa2lVeE9jUkRv

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末板熊,一起剝皮案震驚了整個(gè)濱河市框全,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌干签,老刑警劉巖津辩,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡喘沿,警方通過(guò)查閱死者的電腦和手機(jī)闸度,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)蚜印,“玉大人莺禁,你說(shuō)我怎么就攤上這事≌常” “怎么了哟冬?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)忆绰。 經(jīng)常有香客問(wèn)我浩峡,道長(zhǎng),這世上最難降的妖魔是什么错敢? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任翰灾,我火速辦了婚禮,結(jié)果婚禮上稚茅,老公的妹妹穿的比我還像新娘纸淮。我一直安慰自己,他們只是感情好亚享,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布咽块。 她就那樣靜靜地躺著,像睡著了一般虹蒋。 火紅的嫁衣襯著肌膚如雪糜芳。 梳的紋絲不亂的頭發(fā)上飒货,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天魄衅,我揣著相機(jī)與錄音,去河邊找鬼塘辅。 笑死晃虫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的扣墩。 我是一名探鬼主播哲银,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呻惕!你這毒婦竟也來(lái)了荆责?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤亚脆,失蹤者是張志新(化名)和其女友劉穎做院,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡键耕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年寺滚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片屈雄。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡村视,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出酒奶,到底是詐尸還是另有隱情蚁孔,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布惋嚎,位于F島的核電站勒虾,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏瘸彤。R本人自食惡果不足惜修然,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望质况。 院中可真熱鬧愕宋,春花似錦、人聲如沸结榄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)臼朗。三九已至邻寿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間视哑,已是汗流浹背绣否。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挡毅,地道東北人蒜撮。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像跪呈,于是被迫代替她去往敵國(guó)和親段磨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355