天天看點

Python日志子產品logging

官方文檔:

https://docs.python.org/2/library/logging.html

logging子產品提供了兩種記錄日志的方式:

  • 第一種方式是使用logging提供的子產品級别的函數
  • 第二種方式是使用Logging日志系統的四大元件

其實,logging所提供的子產品級别的日志記錄函數也是對logging日志系統相關類的封裝而已。

logging子產品定義的子產品級别的常用函數:

函數 說明
logging.debug(msg, *args, **kwargs) 建立一條嚴重級别為DEBUG的日志記錄
logging.info(msg, *args, **kwargs) 建立一條嚴重級别為INFO的日志記錄
logging.warning(msg, *args, **kwargs) 建立一條嚴重級别為WARNING的日志記錄
logging.error(msg, *args, **kwargs) 建立一條嚴重級别為ERROR的日志記錄
logging.critical(msg, *args, **kwargs) 建立一條嚴重級别為CRITICAL的日志記錄
logging.log(level, *args, **kwargs) 建立一條嚴重級别為level的日志記錄
logging.basicConfig(**kwargs) 對root logger進行一次性配置

其中

logging.basicConfig(**kwargs)

函數用于指定“要記錄的日志級别”、“日志格式”、“日志輸出位置”、“日志檔案的打開模式”等資訊,其他幾個都是用于記錄各個級别日志的函數。

具體如下:

參數名稱 描述
filename 将日志資訊寫入檔案中,指定該設定項後日志資訊就不會被輸出到控制台了
filemode 指定日志檔案的打開模式,預設為'a'。需要注意的是,該選項要在filename指定時才有效
format 指定日志格式字元串,即指定日志輸出時所包含的字段資訊以及它們的順序。logging子產品定義的格式字段下面會列出。
datefmt 指定日期/時間格式。需要注意的是,該選項要在format中包含時間字段%(asctime)s時才有效。
level 指定日志級别
stream 指定日志輸出目标stream,如sys.stdout、sys.stderr以及網絡stream。需要說明的是,stream和filename不能同時提供,否則會引發 ValueError異常
style Python 3.2中新添加的配置項。指定format格式字元串的風格,可取值為'%'、'{'和'$',預設為'%'
handlers Python 3.3中新添加的配置項。該選項如果被指定,它應該是一個建立了多個Handler的可疊代對象,這些handler将會被添加到root logger。需要說明的是:filename、stream和handlers這三個配置項隻能有一個存在,不能同時出現2個或3個,否則會引發ValueError異常。

上面的時間需要使用format中包含時間段,過于format還有如下參數:

字段/屬性名稱 使用格式
asctime %(asctime)s 日志事件發生的時間--人類可讀時間,如:2003-07-08 16:49:45,896
created %(created)f 日志事件發生的時間--時間戳,就是當時調用time.time()函數傳回的值
relativeCreated %(relativeCreated)d 日志事件發生的時間相對于logging子產品加載時間的相對毫秒數(目前還不知道幹嘛用的)
msecs %(msecs)d 日志事件發生事件的毫秒部分
levelname %(levelname)s 該日志記錄的文字形式的日志級别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
levelno %(levelno)s 該日志記錄的數字形式的日志級别(10, 20, 30, 40, 50)
name %(name)s 所使用的日志器名稱,預設是'root',因為預設使用的是 rootLogger
message %(message)s 日志記錄的文本内容,通過 msg % args計算得到的
pathname %(pathname)s 調用日志記錄函數的源碼檔案的全路徑
%(filename)s pathname的檔案名部分,包含檔案字尾
module %(module)s filename的名稱部分,不包含字尾
lineno %(lineno)d 調用日志記錄函數的源代碼所在的行号
funcName %(funcName)s 調用日志記錄函數的函數名
process %(process)d 程序ID
processName %(processName)s 程序名稱,Python 3.1新增
thread %(thread)d 線程ID
threadName %(thread)s 線程名稱

一、使用logging提供的子產品級别的函數記錄日志

  • 可以通過logging子產品定義的子產品級别的方法去完成簡單的日志記錄
  • 隻有級别大于或等于日志記錄器指定級别的日志記錄才會被輸出,小于該級别的日志記錄将會被丢棄。

logging的日志級别由低到高分為 debug(), info(), warning(), error() and critical() 5個級别

CRITICAL(50) > ERROR(40) > WARNING(30) > INFO(20) > DEBUG(10)

簡單使用示例:

import logging

logging.basicConfig(filename="E:\\logging\\abc.log", format='%(asctime)s %(message)s', datefmt='%Y-%m-%d %I:%M:%S %p', level=logging.WARNING)

logger = logging.getLogger()

logger.debug("The debug.")
logger.info("The info.")
logger.warning("The warning.")
logger.error("The error.")
logger.critical("The critical.")

print(logger.level)      

運作結果:

檢視E:\\logging\\abc.log檔案中的内容,如下:

Python日志子產品logging

可以看到,由于設定的日志級别為WARNING,是以隻列印了比WARNING級别更高的日志。

二、使用logging四大元件記錄日志

logging子產品的四大元件:
logger:提供日志接口,供應用代碼使用。logger最長用的操作有兩類:配置和發送日志消息。可以通過logging.getLogger(name)擷取logger對象,
        如果不指定name則傳回root對象,多次使用相同的name調用getLogger方法傳回同一個logger對象

handler:将日志記錄(log record)發送到合适的目的地(destination),比如檔案,socket等。一個logger對象可以通過addHandler方法添加多個handler,
         每個handler又可以定義不同日志級别,以實作日志分級過濾顯示

filter:提供方式決定一個日志記錄是否發送到handler

formatter:指定日志記錄輸出的具體格式。formatter的構造方法需要兩個參數:消息的格式字元串和日期字元串,這兩個參數都是可選的      

如上面所說,logging.basicConfig()函數中可通過具體參數來更改logging子產品的行為

日志同時列印到螢幕和檔案:

import logging

# 建立一個日志對象
logg = logging.getLogger("測試日志")
# 定義一個模闆
FORMATTER = logging.Formatter("%(asctime)s - %(name)s - [%(lineno)d] - %(message)s")

# 建立一個螢幕流
p_stream = logging.StreamHandler()
# 建立一個檔案流
f_stream = logging.FileHandler("log.log", mode="a", encoding="utf-8")

# 将流綁定到模闆
p_stream.setFormatter(FORMATTER)
f_stream.setFormatter(FORMATTER)

# 将日志和流進行綁定
logg.addHandler(p_stream)
logg.addHandler(f_stream)

# 設定日志記錄等級
logg.setLevel(logging.DEBUG)

# 列印日志資訊
logg.debug("this is Debug")
logg.info("this is info")
logg.warning("this is warning")
logg.error("this is error")
logg.critical("this is critical")      
Python日志子產品logging

如果想為多個使用者建立不同的日志,隻需要建立流時建立不同的流即可,而不需要建立多個日志對象,如果情況特殊考慮單獨在建立一個對象。其實我們可以在建立流的時候直接指定日志等級。流的等級是優先于日志對象的。

如下建立兩個流:

import logging

# 建立一個日志對象
logg = logging.getLogger("測試日志")

# 建立一個程式員模闆和老闆模闆
CORE_FORMATTER = logging.Formatter("%(asctime)s - %(name)s - [%(lineno)d] - %(message)s")
BOOS_FORMATTER = logging.Formatter("%(asctime)s - %(message)s")

# 建立一個程式員的流和一個老闆的流
core_stream = logging.FileHandler("core.log", mode="a", encoding="utf-8")
boos_stream = logging.FileHandler("boos.log", mode="a", encoding="utf-8")

# 設定日志等級(老闆的日志等級設定WARNING)
boos_stream.setLevel(logging.WARNING)

# 将流綁定到模闆
core_stream.setFormatter(CORE_FORMATTER)
boos_stream.setFormatter(BOOS_FORMATTER)

# 将日志和流進行綁定
logg.addHandler(core_stream)
logg.addHandler(boos_stream)

# 設定日志記錄等級
logg.setLevel(logging.DEBUG)

# 列印日志資訊
logg.debug("this is Debug")
logg.info("this is info")
logg.warning("this is warning")
logg.error("this is error")
logg.critical("this is critical")      
Python日志子產品logging
Python日志子產品logging

這樣我們便建立了兩種不同的日志

三、日志分割

将日志資訊輸出到一個單一的檔案中,随着應用程式的持續使用,該日志檔案會越來越龐大,進而影響系統的性能。是以,有必要對日志檔案按某種條件進行切分。

分割日志的觸發條件:大小、日期,或者大小加上日期。

說是切分,實際上是,當一個日志檔案達到觸發條件後,對日志檔案進行重命名,之後再建立原來名稱的日志檔案(此時就是空檔案了),新産生的日志就寫入新的日志檔案。

為啥叫復原呢?當分割的日志檔案達到指定數目的上限個數時,最老的日志檔案就會被删除。

logging庫提供了2個可以用于日志滾動的class,一個是RotatingFileHandler,它主要是根據日志檔案的大小進行滾動;另一個是TimeRotatingFileHandler,它主要是根據時間進行滾動。在實際應用中,通常根據時間進行滾動。 

TimedRotatingFileHandler的構造函數定義如下:

TimedRotatingFileHandler(filename [,when [,interval [,backupCount]]])

filename 是輸出日志檔案名的字首,比如log/myapp.log

when 的定義如下:

“S”: Seconds

“M”: Minutes

“H”: Hours

“D”: Days

“W”: Week day (0=Monday)

“midnight”: Roll over at midnight

interval:指等待多少個機關when的時間後,Logger會自動重建檔案,當然,這個檔案的建立取決于filename+suffix,若這個檔案跟之前的檔案有重名,則會自動覆寫掉以前的檔案,是以有些情況suffix要定義的不能因為when而重複。

backupCount:保留日志個數。預設的0是不會自動删除掉日志。若設3,則在檔案的建立過程中庫會判斷是否有超過這個3,若超過,則會從最先建立的開始删除。      

按秒切分示例:

import time
import logging
import logging.handlers
import os

# 如果日志檔案夾不存在,則建立
log_dir = "log-second"  # 日志存放檔案夾名稱
log_path = os.getcwd() + os.sep + log_dir
if not os.path.isdir(log_path):
    os.makedirs(log_path)

# logging初始化工作
logging.basicConfig()

# 初始化loggger
test = logging.getLogger('test')
test.setLevel(logging.INFO)

# 添加TimedRotatingFileHandler
# 定義一個1秒換一次log檔案的handler
# 保留3個舊log檔案
timefilehandler = logging.handlers.TimedRotatingFileHandler(
    log_dir + os.sep + "log",
    when='S',
    interval=1,
    backupCount=3
)

# 設定字尾名稱,跟strftime的格式一樣
timefilehandler.suffix = "%Y-%m-%d_%H-%M-%S.log"

formatter = logging.Formatter('%(asctime)s|%(name)-12s: %(levelname)-8s %(message)s')
timefilehandler.setFormatter(formatter)
test.addHandler(timefilehandler)

n = 6
while n > 0:
    test.info("這是一個時間分割的測試程式")
    time.sleep(1)
    n -= 1      
Python日志子產品logging

注意:

timefilehandler.suffix的設定一定要和時間機關相符,不如按秒切分,就必須設定timefilehandler.suffix= "%Y-%m-%d_%H-%M-%S.log",否則就不能删除舊檔案了。按天、按分鐘切分也是如此。

RotatingFileHandler基于檔案大小切分

示例:

import time
# import logging
import os
import logging.handlers

# 如果日志檔案夾不存在,則建立
log_dir = "log-size"  # 日志存放檔案夾名稱
log_path = os.getcwd() + os.sep + log_dir
if not os.path.isdir(log_path):
    os.makedirs(log_path)

# logging初始化工作
logging.basicConfig()

# 初始化loggger
test_02 = logging.getLogger('test_02')
test_02.setLevel(logging.INFO)

# 寫入檔案,如果單個檔案超過100個Bytes,則寫入下一個檔案,最多保留5個檔案
handler = logging.handlers.RotatingFileHandler(
    'log-size/test_02.log', maxBytes=100, backupCount=5)

formatter = logging.Formatter('%(asctime)s|%(name)-12s: %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
# 設定字尾名稱,跟strftime的格式一樣
test_02.addHandler(handler)

n = 6
while n > 0:
    test_02.info("這是一個大小分割的測試程式")
    time.sleep(1)
    n -= 1      
Python日志子產品logging
Python日志子產品logging

-----------------------------------------------------------------------------

推薦:

詳細全面:https://www.cnblogs.com/yyds/p/6901864.html

https://www.cnblogs.com/hanmk/p/10448963.html