程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

python + logging 實現日志輸出及保存到文件

編輯:Python

文章目錄
  • 1 開始使用 logging
    • 1.1 第一個程序
    • 1.2 日志級別
    • 1.3 輸出格式
  • 2 輸出日志到文件
    • 2.1 使用 basicConfig 配置文件路徑
    • 2.2 logging 模塊化設計
    • 2.3 自動分割日志文件

最近因為一個小需求,需要保存日志到文件中。因為平時調試都只是用print,當不需要的時候又得把print刪掉,這樣很不方便,而且這樣也只能把報錯信息輸出到控制台。於是上網查了一下,python有一個內置模塊logging,用來輸出日志信息,可以進行各種配置,看了之後有種相見恨晚的感覺。下面進行一些個人的總結,主要是對自己學習進行的歸納,也希望能對你有所幫助。

1 開始使用 logging

1.1 第一個程序

首先是最簡單的使用:

# -*- coding: utf-8 -*-
import logging
logging.debug('debug級別,一般用來打印一些調試信息,級別最低')
logging.info('info級別,一般用來打印一些正常的操作信息')
logging.warning('waring級別,一般用來打印警告信息')
logging.error('error級別,一般用來打印一些錯誤信息')
logging.critical('critical級別,一般用來打印一些致命的錯誤信息,等級最高')

這樣直接就可以在控制台輸出日志信息了:

WARNING:root:waring級別,一般用來打印警告信息
ERROR:root:error級別,一般用來打印一些錯誤信息
CRITICAL:root:critical級別,一般用來打印一些致命的錯誤信息,等級最高

1.2 日志級別

會發現只輸出下面三條信息,這是因為logging是分級別的,上面5個級別的信息從上到下依次遞增,可以通過設置logging的level,使其只打印某個級別以上的信息。因為默認等級是 WARNING,所以只有 WARNING 以上級別的日志被打印出來。 如果我們想把debug和info也打印出來,可以使用 basicConfig 對其進行配置:

logging.basicConfig(level=logging.DEBUG)

這樣控制台的輸出就會包含上面5條所有信息。

日志級別不是只有python才有,基本上日志都是分級別的,這樣可以讓我們在不同的時期關注不同的重點,比如我們把一些調試的信息以debug的級別輸出,並且把 logging 的 level 設為 DEBUG,這樣我們以後不需要顯示這些日志的時候,只需要把level設置為info或者更高,不用像 print 一樣要去把那條語句注釋掉或者刪掉。

1.3 輸出格式

我們發現上面的日志輸出信息很簡略,暫時還不能滿足我們的需求,比如我們可能需要輸出該條信息的時間,所在位置等等,這同樣可以通過basicConfig進行配置。

logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.DEBUG)

然後輸出就會是這樣的格式:

2019-07-19 15:54:26,625 - log_test.py[line:11] - DEBUG: debug級別,一般用來打印一些調試信息,級別最低

format 可以指定輸出的內容和格式,其內置的參數如下:

%(name)s:Logger的名字
%(levelno)s:打印日志級別的數值
%(levelname)s:打印日志級別的名稱
%(pathname)s:打印當前執行程序的路徑,其實就是sys.argv[0]
%(filename)s:打印當前執行程序名
%(funcName)s:打印日志的當前函數
%(lineno)d:打印日志的當前行號
%(asctime)s:打印日志的時間
%(thread)d:打印線程ID
%(threadName)s:打印線程名稱
%(process)d:打印進程ID
%(message)s:打印日志信息

此外,basicConfig 還可以進行許多其他配置,後文繼續介紹。

2 輸出日志到文件

2.1 使用 basicConfig 配置文件路徑

以上我們只是把日志輸出到控制台,但很多時候我們可能會需要把日志存到文件,這樣程序出現問題時,可以方便我們根據日志信息進行定位。 最簡單的方式是使用 basicConfig:

logging.basicConfig(format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s',
level=logging.DEBUG,
filename='test.log',
filemode='a')

只是在上面配置的基礎上加上filenamefilemode參數,這樣就可以把日志輸出到 test.log 文件中了,如果沒有這個文件的話會自動創建。 其中參數 filemode表示文件打開模式,不設的話默認為’a’,即追加模式,可以不設;也可以設為’w’,每次寫日志會覆蓋之前的日志。 但是進行這樣的操作之後,我們會發現控制台不輸出了,怎麼做到既輸出到控制台又寫入到文件呢? 這需要更進一步的學習。

2.2 logging 模塊化設計

以上我們只是使用logging進行非常簡單的操作,但這樣作用有限,其實 logging 庫采取了模塊化的設計,提供了許多組件:記錄器、處理器、過濾器和格式化器。

  • Logger 暴露了應用程序代碼能直接使用的接口。
  • Handler 將(記錄器產生的)日志記錄發送至合適的目的地。
  • Filter 提供了更好的粒度控制,它可以決定輸出哪些日志記錄。
  • Formatter 指明了最終輸出中日志記錄的內容和格式。

簡單地說,其中 Logger 是負責記錄日志消息的,然後我們要把這些日志消息放到哪裡,交給 Handler 處理,Filter 則幫我們過濾信息(不限於通過級別過濾),Formatter 就是跟上面的 format 一個意思,用來設置日志內容和格式。

這樣,我們試一下使用模塊的方式,重新記錄日志:

logger = logging.getLogger('test')
logger.debug('debug級別,一般用來打印一些調試信息,級別最低')
logger.info('info級別,一般用來打印一些正常的操作信息')
logger.warning('waring級別,一般用來打印警告信息')
logger.error('error級別,一般用來打印一些錯誤信息')
logger.critical('critical級別,一般用來打印一些致命的錯誤信息,等級最高')

首先第一行 getLogger 獲取了一個記錄器,其中命名標識了這個 Logger。然後下面的輸出方式跟我們一開始 logging 的用法是很相似的,看起來是不是很簡單。但這樣是不行,運行後會報錯:

No handlers could be found for logger "test"

是說我們沒有為這個logger指定handler,它不知道要怎麼處理日志,要輸出到哪裡去。那我們就給他加一個Handler吧,Handler的種類有很多,常用的有4種:

  • logging.StreamHandler -> 控制台輸出
  • logging.FileHandler -> 文件輸出
  • logging.handlers.RotatingFileHandler -> 按照大小自動分割日志文件,一旦達到指定的大小重新生成文件
  • logging.handlers.TimedRotatingFileHandler -> 按照時間自動分割日志文件

現在我們先使用最簡單的StreamHandler把日志輸出到控制台:

logger = logging.getLogger('test')
stream_handler = logging.StreamHandler()
logger.addHandler(stream_handler)
...

這樣就可以在控制台看到:

waring級別,一般用來打印警告信息
error級別,一般用來打印一些錯誤信息
critical級別,一般用來打印一些致命的錯誤信息,等級最高

還是少了幾條日志,因為我們沒有設置日志級別,我們同樣設置一下級別,並且也使用Formatter模塊設置一下輸出格式。

logger = logging.getLogger('test')
logger.setLevel(level=logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(formatter)
logger.addHandler(stream_handler)
...

我們發現Formatter是給handler設置的,這很好理解,因為handler是負責把日志輸出到哪裡,所以是給它設置格式,而不是給logger;那為什麼level需要設置兩次呢?給logger設置是告訴它要記錄哪些級別的日志,給handler設是告訴它要輸出哪些級別的日志,相當於進行了兩次過濾。這樣的好處在於,當我們有多個日志去向時,比如既保存到文件,又輸出到控制台,就可以分別給他們設置不同的級別;logger 的級別是先過濾的,所以被 logger 過濾的日志 handler 也是無法記錄的,這樣就可以只改 logger 的級別而影響所有輸出。兩者結合可以更方便地管理日志記錄的級別。

有了handler,我們就可以很方便地同時將日志輸出到控制台和文件:

logger = logging.getLogger('test')
logger.setLevel(level=logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s')
file_handler = logging.FileHandler('test2.log')
file_handler.setLevel(level=logging.INFO)
file_handler.setFormatter(formatter)
stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(formatter)
logger.addHandler(file_handler)
logger.addHandler(stream_handler)

只需要多加一個FileHandler即可。

2.3 自動分割日志文件

有時候我們需要對日志文件進行分割,以方便我們的管理。python 提供了兩個處理器,方便我們分割文件:

  • logging.handlers.RotatingFileHandler -> 按照大小自動分割日志文件,一旦達到指定的大小重新生成文件
  • logging.handlers.TimedRotatingFileHandler -> 按照時間自動分割日志文件

使用方法跟上面的 Handler 類似,只是需要添加一些參數配置,比如when='D'表示以天為周期切分文件,其他參數的意思可以參考:Python + logging 輸出到屏幕,將log日志寫入文件

from logging import handlers
time_rotating_file_handler = handlers.TimedRotatingFileHandler(filename='rotating_test.log', when='D')
time_rotating_file_handler.setLevel(logging.DEBUG)
time_rotating_file_handler.setFormatter(formatter)
logger.addHandler(time_rotating_file_handler)

若改為when='S',則以秒為周期進行切割,運行幾次後會生成文件:

其中沒有後綴的為最新日志文件。

參考文章: Python + logging 輸出到屏幕,將log日志寫入文件Python標准模塊–logging


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved