這篇文章主要介紹“Python3 Loguru輸出日志工具如何使用”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強(qiáng),希望這篇“Python3 Loguru輸出日志工具如何使用”文章能幫助大家解決問題。
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價值的長期合作伙伴,公司提供的服務(wù)項目有:域名申請、雅安服務(wù)器托管、營銷軟件、網(wǎng)站建設(shè)、新巴爾虎右網(wǎng)站維護(hù)、網(wǎng)站推廣。
Python logging 模塊定義了為應(yīng)用程序和庫實現(xiàn)靈活的事件日志記錄的函數(shù)和類。
程序開發(fā)過程中,很多程序都有記錄日志的需求,并且日志包含的信息有正常的程序訪問日志還可能有錯誤、警告等信息輸出,Python 的 logging 模塊提供了標(biāo)準(zhǔn)的日志接口,可以通過它存儲各種格式的日志,日志記錄提供了一組便利功能,用于簡單的日志記錄用法。
使用 Python Logging 模塊的主要好處是所有 Python 模塊都可以參與日志記錄Logging 模塊提供了大量具有靈活性的功能。
簡單且方便的幫助我們輸出需要的日志信息:
使用 Python 來寫程序或者腳本的話,常常遇到的問題就是需要對日志進(jìn)行刪除。一方面可以幫助我們在程序出問題的時候排除問題,二來可以幫助我們記錄需要關(guān)注的信息。
但是,使用自帶自帶的 logging 模塊的話,則需要我們進(jìn)行不同的初始化等相關(guān)工作。對應(yīng)不熟悉該模塊的同學(xué)來說,還是有些費勁的,比如需要配置 Handler/Formatter 等。 隨著業(yè)務(wù)的復(fù)雜度提升, 對日志收集有著更高的要求, 例如: 日志分類, 文件存儲, 異步寫入, 自定義類型等等
loguru 是一個 Python 簡易且強(qiáng)大的第三方日志記錄庫,該庫旨在通過添加一系列有用的功能來解決標(biāo)準(zhǔn)記錄器的注意事項,從而減少 Python 日志記錄的痛苦。
pip install loguru
有很多優(yōu)點,以下列舉了其中比較重要的幾點:
開箱即用,無需準(zhǔn)備
無需初始化,導(dǎo)入函數(shù)即可使用
更容易的文件日志記錄與轉(zhuǎn)存/保留/壓縮方式
更優(yōu)雅的字符串格式化輸出
可以在線程或主線程中捕獲異常
可以設(shè)置不同級別的日志記錄樣式
支持異步,且線程和多進(jìn)程安全
支持惰性計算
適用于腳本和庫
完全兼容標(biāo)準(zhǔn)日志記錄
更好的日期時間處理
from loguru import logger logger.debug("That's it, beautiful and simple logging!")
無需初始化,導(dǎo)入函數(shù)即可使用, 那么你肯定要問, 如何解決一下問題?
如何添加處理程序(handler)呢?
如何設(shè)置日志格式(logs formatting)呢?
如何過濾消息(filter messages)呢?
如何如何設(shè)置級別(log level)呢?
# add logger.add(sys.stderr, \ format="{time} {level} {message}",\ filter="my_module",\ level="INFO")
是不是很easy~
# 日志文件記錄 logger.add("file_{time}.log") # 日志文件轉(zhuǎn)存 logger.add("file_{time}.log", rotation="500 MB") logger.add("file_{time}.log", rotation="12:00") logger.add("file_{time}.log", rotation="1 week") # 多次時間之后清理 logger.add("file_X.log", retention="10 days") # 使用zip文件格式保存 logger.add("file_Y.log", compression="zip")
logger.info( "If you're using Python {}, prefer {feature} of course!", 3.10, feature="f-strings")
@logger.catch def my_function(x, y, z): # An error? It's caught anyway! return 1 / (x + y + z) my_function(0, 0, 0)
Loguru 會自動為不同的日志級別,添加不同的顏色進(jìn)行區(qū)分, 也支持自定義顏色哦~
logger.add(sys.stdout, colorize=True, format="<green>{time}</green> <level>{message}</level>") logger.add('logs/z_{time}.log', level='DEBUG', format='{time:YYYY-MM-DD :mm:ss} - {level} - {file} - {line} - {message}', rotation="10 MB")
默認(rèn)情況下,添加到 logger 中的日志信息都是線程安全的。但這并不是多進(jìn)程安全的,我們可以通過添加 enqueue 參數(shù)來確保日志完整性。
如果我們想要在異步任務(wù)中使用日志記錄的話,也是可以使用同樣的參數(shù)來保證的。并且通過 complete() 來等待執(zhí)行完成。
# 異步寫入 logger.add("some_file.log", enqueue=True)
你沒有看錯, 只需要enqueue=True
即可異步執(zhí)行
用于記錄代碼中發(fā)生的異常的 bug 跟蹤,Loguru 通過允許顯示整個堆棧跟蹤(包括變量值)來幫助您識別問題
logger.add("out.log", backtrace=True, diagnose=True) def func(a, b): return a / b def nested(c): try: func(5, c) except ZeroDivisionError: logger.exception("What?!") nested(0)
對日志進(jìn)行序列化以便更容易地解析或傳遞數(shù)據(jù)結(jié)構(gòu),使用序列化參數(shù),在將每個日志消息發(fā)送到配置的接收器之前,將其轉(zhuǎn)換為 JSON 字符串。
同時,使用 bind() 方法,可以通過修改額外的 record 屬性來將日志記錄器消息置于上下文中。還可以通過組合 bind() 和 filter 對日志進(jìn)行更細(xì)粒度的控制。
最后 patch() 方法允許將動態(tài)值附加到每個新消息的記錄 dict 上。
# 序列化為json格式 logger.add(custom_sink_function, serialize=True) # bind方法的用處 logger.add("file.log", format="{extra[ip]} {extra[user]} {message}") context_logger = logger.bind(ip="192.168.2.174", user="someone") context_logger.info("Contextualize your logger easily") context_logger.bind(user="someone_else").info("Inline binding of extra attribute") context_logger.info("Use kwargs to add context during formatting: {user}", user="anybody") # 粒度控制 logger.add("special.log", filter=lambda record: "special" in record["extra"]) logger.debug("This message is not logged to the file") logger.bind(special=True).info("This message, though, is logged to the file!") # patch()方法的用處 logger.add(sys.stderr, format="{extra[utc]} {message}") loggerlogger = logger.patch(lambda record: record["extra"].update(utc=datetime.utcnow()))
有時希望在生產(chǎn)環(huán)境中記錄詳細(xì)信息而不會影響性能,可以使用 opt() 方法來實現(xiàn)這一點。
logger.opt(lazy=True).debug("If sink level <= DEBUG: {x}", x=lambda: expensive_function(2**64)) # By the way, "opt()" serves many usages logger.opt(exception=True).info("Error stacktrace added to the log message (tuple accepted too)") logger.opt(colors=True).info("Per message <blue>colors</blue>") logger.opt(record=True).info("Display values from the record (eg. {record[thread]})") logger.opt(raw=True).info("Bypass sink formatting\n") logger.opt(depth=1).info("Use parent stack context (useful within wrapped functions)") logger.opt(capture=False).info("Keyword arguments not added to {dest} dict", dest="extra")
new_level = logger.level("SNAKY", no=38, color="<yellow>", icon="????") logger.log("SNAKY", "Here we go!")
# For scripts config = { "handlers": [ {"sink": sys.stdout, "format": "{time} - {message}"}, {"sink": "file.log", "serialize": True}, ], "extra": {"user": "someone"} } logger.configure(**config) # For libraries logger.disable("my_library") logger.info("No matter added sinks, this message is not displayed") logger.enable("my_library") logger.info("This message however is propagated to the sinks")
希望使用 Loguru 作為內(nèi)置的日志處理程序?
需要將 Loguru 消息到標(biāo)準(zhǔn)日志?
想要攔截標(biāo)準(zhǔn)的日志消息到 Loguru 中匯總?
handler = logging.handlers.SysLogHandler(address=('localhost', 514)) logger.add(handler) class PropagateHandler(logging.Handler): def emit(self, record): logging.getLogger(record.name).handle(record) logger.add(PropagateHandler(), format="{message}") class InterceptHandler(logging.Handler): def emit(self, record): # Get corresponding Loguru level if it exists try: level = logger.level(record.levelname).name except ValueError: level = record.levelno # Find caller from where originated the logged message frame, depth = logging.currentframe(), 2 while frame.f_code.co_filename == logging.__file__: frameframe = frame.f_back depth += 1 logger.opt(depthdepth=depth, exception=record.exc_info).log(level, record.getMessage()) logging.basicConfig(handlers=[InterceptHandler()], level=0)
從生成的日志中提取特定的信息通常很有用,這就是為什么 Loguru 提供了一個 parse() 方法來幫助處理日志和正則表達(dá)式。
pattern = r"(?P<time>.*) - (?P<level>[0-9]+) - (?P<message>.*)" # Regex with named groups caster_dict = dict(time=dateutil.parser.parse, level=int) # Transform matching groups for groups in logger.parse("file.log", pattern, cast=caster_dict): print("Parsed:", groups) # {"level": 30, "message": "Log example", "time": datetime(2018, 12, 09, 11, 23, 55)}
import notifiers params = { "username": "you@gmail.com", "password": "abc123", "to": "dest@gmail.com" } # Send a single notification notifier = notifiers.get_notifier("gmail") notifier.notify(message="The application is running!", **params) # Be alerted on each error message from notifiers.logging import NotificationHandler handler = NotificationHandler("gmail", defaults=params) logger.add(handler, level="ERROR")
現(xiàn)在最關(guān)鍵的一個問題是如何兼容別的 logger,比如說 tornado 或者 django 有一些默認(rèn)的 logger。
經(jīng)過研究,最好的解決方案是參考官方文檔的,完全整合 logging 的工作方式。比如下面將所有的 logging都用 loguru 的 logger 再發(fā)送一遍消息。
import logging import sys from pathlib import Path from flask import Flask from loguru import logger app = Flask(__name__) class InterceptHandler(logging.Handler): def emit(self, record): loggerlogger_opt = logger.opt(depth=6, exception=record.exc_info) logger_opt.log(record.levelname, record.getMessage()) def configure_logging(flask_app: Flask): """配置日志""" path = Path(flask_app.config['LOG_PATH']) if not path.exists(): path.mkdir(parents=True) log_name = Path(path, 'sips.log') logging.basicConfig(handlers=[InterceptHandler(level='INFO')], level='INFO') # 配置日志到標(biāo)準(zhǔn)輸出流 logger.configure(handlers=[{"sink": sys.stderr, "level": 'INFO'}]) # 配置日志到輸出到文件 logger.add(log_name, rotation="500 MB", encoding='utf-8', colorize=False, level='INFO')
介紹,主要函數(shù)的使用方法和細(xì)節(jié) - add()的創(chuàng)建和刪除
add() 非常重要的參數(shù) sink 參數(shù)
具體的實現(xiàn)規(guī)范可以參見官方文檔
可以實現(xiàn)自定義 Handler 的配置,比如 FileHandler、StreamHandler 等等
可以自行定義輸出實現(xiàn)
代表文件路徑,會自動創(chuàng)建對應(yīng)路徑的日志文件并將日志輸出進(jìn)去
例如 sys.stderr 或者 open(‘file.log’, ‘w’) 都可以
可以傳入一個 file 對象
可以直接傳入一個 str 字符串或者 pathlib.Path 對象
可以是一個方法
可以是一個 logging 模塊的 Handler
可以是一個自定義的類
def add(self, sink, *, level=_defaults.LOGURU_LEVEL, format=_defaults.LOGURU_FORMAT, filter=_defaults.LOGURU_FILTER, colorize=_defaults.LOGURU_COLORIZE, serialize=_defaults.LOGURU_SERIALIZE, backtrace=_defaults.LOGURU_BACKTRACE, diagnose=_defaults.LOGURU_DIAGNOSE, enqueue=_defaults.LOGURU_ENQUEUE, catch=_defaults.LOGURU_CATCH, **kwargs ):
另外添加 sink 之后我們也可以對其進(jìn)行刪除,相當(dāng)于重新刷新并寫入新的內(nèi)容。刪除的時候根據(jù)剛剛 add 方法返回的 id 進(jìn)行刪除即可。可以發(fā)現(xiàn),在調(diào)用 remove 方法之后,確實將歷史 log 刪除了。但實際上這并不是刪除,只不過是將 sink 對象移除之后,在這之前的內(nèi)容不會再輸出到日志中,這樣我們就可以實現(xiàn)日志的刷新重新寫入操作
from loguru import logger trace = logger.add('runtime.log') logger.debug('this is a debug message') logger.remove(trace) logger.debug('this is another debug message')
我們在開發(fā)流程中, 通過日志快速定位問題, 高效率解決問題, 我認(rèn)為 loguru 能幫你解決不少麻煩, 趕快試試吧~
當(dāng)然, 使用各種也有不少麻煩, 例如:
--- Logging error in Loguru Handler #3 ---
Record was: None
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/loguru/_handler.py", line 272, in _queued_writer
message = queue.get()
File "/usr/local/lib/python3.9/multiprocessing/queues.py", line 366, in get
res = self._reader.recv_bytes()
File "/usr/local/lib/python3.9/multiprocessing/connection.py", line 221, in recv_bytes
buf = self._recv_bytes(maxlength)
File "/usr/local/lib/python3.9/multiprocessing/connection.py", line 419, in _recv_bytes
buf = self._recv(4)
File "/usr/local/lib/python3.9/multiprocessing/connection.py", line 384, in _recv
chunk = read(handle, remaining)
OSError: [Errno 9] Bad file descriptor
--- End of logging error ---
解決辦法:
嘗試將logs文件夾忽略git提交, 避免和服務(wù)器文件沖突即可;
當(dāng)然也不止這個原因引起這個問題, 也可能是三方庫(ciscoconfparse)沖突所致.解決辦法: https://github.com/Delgan/loguru/issues/534
File "/home/ronaldinho/xxx/xxx/venv/lib/python3.9/site-packages/loguru/_logger.py", line 939, in add
handler = Handler(
File "/home/ronaldinho/xxx/xxx/venv/lib/python3.9/site-packages/loguru/_handler.py", line 86, in __init__
self._queue = multiprocessing.SimpleQueue()
File "/home/ronaldinho/.pyenv/versions/3.9.4/lib/python3.9/multiprocessing/context.py", line 113, in SimpleQueue
return SimpleQueue(ctx=self.get_context())
File "/home/ronaldinho/.pyenv/versions/3.9.4/lib/python3.9/multiprocessing/queues.py", line 342, in __init__
self._rlock = ctx.Lock()
File "/home/ronaldinho/.pyenv/versions/3.9.4/lib/python3.9/multiprocessing/context.py", line 68, in Lock
return Lock(ctx=self.get_context())
File "/home/ronaldinho/.pyenv/versions/3.9.4/lib/python3.9/multiprocessing/synchronize.py", line 162, in __init__
File "/home/ronaldinho/.pyenv/versions/3.9.4/lib/python3.9/multiprocessing/synchronize.py", line 57, in __init__
OSError: [Errno 24] Too many open files
你可以 remove()添加的處理程序,它應(yīng)該釋放文件句柄。
關(guān)于“Python3 Loguru輸出日志工具如何使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。
文章標(biāo)題:Python3?Loguru輸出日志工具如何使用
標(biāo)題路徑:http://chinadenli.net/article40/gshgho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、、靜態(tài)網(wǎng)站、Google、虛擬主機(jī)、電子商務(wù)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)