loguru包的使用
= 'INFO'
if not os.path.exists(logs_path):
os.makedirs(logs_path)
if not os.path.exists(data_path):
os.makedirs(data_path)
if not os.path.exists(conf_path):
os.makedirs(conf_path)
logger.remove(handler_id=None)
logger.add(
sys.stdout,
colorize=True,
enqueue=True,
level=log_level
)
logger.add(
os.path.join(logs_path, 'hot_news.trace.log'),
filter=lambda x: 'TRACE' in str(x['level']).upper(),
enqueue=True,
rotation="00:00",
level=log_level
)
logger.add(
os.path.join(logs_path, 'hot_news.debug.log'),
filter=lambda x: 'DEBUG' in str(x['level']).upper(),
enqueue=True,
rotation="00:00",
level=log_level
)
logger.add(
os.path.join(logs_path, 'hot_news.info.log'),
filter=lambda x: 'INFO' in str(x['level']).upper(),
enqueue=True,
rotation="00:00",
level=log_level
)
logger.add(
os.path.join(logs_path, 'hot_news.error.log'),
filter=lambda x: 'ERROR' in str(x['level']).upper(),
enqueue=True,
rotation="00:00",
level=log_level
)
詳細說明
既然是日志,那麼最常見的就是輸出到檔案了。loguru 對輸出到檔案的配置有非常強大的支援,比如支援輸出到多個檔案,分級别分别輸出,過大建立新檔案,過久自動删除等等。 下面我們分别看看這些怎樣來實作,這裡基本上就是 add 方法的使用介紹。因為這個 add 方法就相當于給 logger 添加了一個 Handler,它給我們暴露了許多參數來實作 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
):
pass
看看它的源代碼,它支援這麼多的參數,如 level、format、filter、color 等等,另外我們還注意到它有個非常重要的參數 sink,我們看看官方文檔:https://loguru.readthedocs.io/en/stable/api/logger.html#sink
可以通過sink,我們可以傳入多種不同的資料結構,彙總如下:
- sink 可以傳入一個 file 對象,例如
或者sys.stderr
都可以。open('file.log', 'w')
- sink 可以直接傳入一個
字元串或者str
對象,其實就是代表檔案路徑的,如果識别到是這種類型,它會自動建立對應路徑的日志檔案并将日志輸出進去。pathlib.Path
- sink 可以是一個方法,可以自行定義輸出實作。
- sink 可以是一個 logging 子產品的 Handler,比如 FileHandler、StreamHandler 等等,或者上文中我們提到的 CMRESHandler 照樣也是可以的,這樣就可以實作自定義 Handler 的配置。
- sink 還可以是一個自定義的類,具體的實作規範可以參見官方文檔。
是以說,剛才我們所示範的輸出到檔案,僅僅給它傳了一個 str 字元串路徑,他就給我們建立了一個日志檔案,就是這個原理。
上述代碼中我們将INFO級别的日志打到了sys.stdout中,可以在輸出流中看到INFO的日志
基本參數
下面我們再了解下它的其他參數,例如 format、filter、level 等等。 其實它們的概念和格式和 logging 子產品都是基本一樣的了,例如這裡使用 format、filter、level 來規定輸出的格式:
logger.add('runtime.log', format="{time} {level} {message}", filter="my_module", level="INFO")
删除 sink
另外添加 sink 之後我們也可以對其進行删除,相當于重新重新整理并寫入新的内容。 删除的時候根據剛剛 add 方法傳回的 id 進行删除即可,看下面的例子:
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')
看這裡,我們首先 add 了一個 sink,然後擷取它的傳回值,指派為 trace。随後輸出了一條日志,然後将 trace 變量傳給 remove 方法,再次輸出一條日志,看看結果是怎樣的。 控制台輸出如下:
2019-10-13 23:18:26.469 | DEBUG | __main__:<module>:4 - this is a debug message
2019-10-13 23:18:26.469 | DEBUG | __main__:<module>:6 - this is
日志檔案 runtime.log 内容如下:
2019-10-13 23:18:26.469 | DEBUG | __main__:<module>:4 - this is
可以發現,在調用 remove 方法之後,确實将曆史 log 删除了。但實際上這并不是删除,隻不過是将 sink 對象移除之後,在這之前的内容不會再輸出到日志中。 這樣我們就可以實作日志的重新整理重新寫入操作。
rotation 配置
用了 loguru 我們還可以非常友善地使用 rotation 配置,比如我們想一天輸出一個日志檔案,或者檔案太大了自動分隔日志檔案,我們可以直接使用 add 方法的 rotation 參數進行配置。 我們看看下面的例子:
logger.add('runtime_{time}.log', rotation="500 MB")
通過這樣的配置我們就可以實作每 500MB 存儲一個檔案,每個 log 檔案過大就會新建立一個 log 檔案。我們在配置 log 名字時加上了一個 time 占位符,這樣在生成時可以自動将時間替換進去,生成一個檔案名包含時間的 log 檔案。 另外我們也可以使用 rotation 參數實作定時建立 log 檔案,例如:
logger.add('runtime_{time}.log', rotation='00:00')
這樣就可以實作每天 0 點新建立一個 log 檔案輸出了。 另外我們也可以配置 log 檔案的循環時間,比如每隔一周建立一個 log 檔案,寫法如下:
logger.add('runtime_{time}.log', rotation='1 week')
這樣我們就可以實作一周建立一個 log 檔案了。
retention 配置
很多情況下,一些非常久遠的 log 對我們來說并沒有什麼用處了,它白白占據了一些存儲空間,不清除掉就會非常浪費。retention 這個參數可以配置日志的最長保留時間。 比如我們想要設定日志檔案最長保留 10 天,可以這麼來配置:
logger.add('runtime.log', retention='10 days')
這樣 log 檔案裡面就會保留最新 10 天的 log,媽媽再也不用擔心 log 沉積的問題啦。
compression 配置
loguru 還可以配置檔案的壓縮格式,比如使用 zip 檔案格式儲存,示例如下:
logger.add('runtime.log', compression='zip')
這樣可以更加節省存儲空間。
字元串格式化
loguru 在輸出 log 的時候還提供了非常友好的字元串格式化功能,像這樣:
logger.info('If you are using Python {}, prefer {feature} of course!', 3.6, feature='f-strings')
這樣在添加參數就非常友善了。
Traceback 記錄
在很多情況下,如果遇到運作錯誤,而我們在列印輸出 log 的時候萬一不小心沒有配置好 Traceback 的輸出,很有可能我們就沒法追蹤錯誤所在了。 但用了 loguru 之後,我們用它提供的裝飾器就可以直接進行 Traceback 的記錄,類似這樣的配置即可:
@logger.catch
def my_function(x, y, z):
# An error? It's caught anyway!
return 1 / (x + y + z)
我們做個測試,我們在調用時三個參數都傳入 0,直接引發除以 0 的錯誤,看看會出現什麼情況:
my_function(0, 0, 0)
\> File "run.py", line 15, in <module>
my_function(0, 0, 0)
└ <function my_function at 0x1171dd510>
File "/private/var/py/logurutest/demo5.py", line 13, in my_function
return 1 / (x + y + z)
│ │ └ 0
│ └ 0
└ 0
ZeroDivisionError: