logging模塊簡單使用&多進程下使用

2021-02-14 MUL筆記

python自動了logging模塊,可以方便的記錄和列印日誌

大致的思路

import logging

# 定義日誌輸出格式
formattler = '%(levelname)s - %(name)s - %(asctime)s - %(message)s'
fmt = logging.Formatter(formattler)

# 獲得logger,默認獲得root logger對象
# 設置logger級別 debug
# root logger默認的級別是warning級別。
# 不設置的話 只能發送 >= warning級別的日誌
logger = logging.getLogger()
logger.setLevel(logging.DEBUG)
# 設置handleer日誌處理器,日誌具體怎麼處理都在日誌處理器裡面定義
# SteamHandler 流處理器,輸出到控制臺,輸出方式為stdout
#   StreamHandler默認輸出到sys.stderr
# 設置handler所處理的日誌級別。
#   只能處理 >= 所設置handler級別的日誌
# 設置日誌輸出格式
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler.setLevel(logging.DEBUG)
stream_handler.setFormatter(fmt)


file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(fmt)

# 對logger增加handler日誌處理器
logger.addHandler(stream_handler)

其中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: 列印日誌信息

在程序模塊直接使用就可以可以了

def test(num):
    time.sleep(3)
    logger.debug('日誌測試' + str(num))

多進程下logger使用

自帶的logger不支持多進程,因為涉及到進程間通信等問題。所以需要進行特殊處理,這裡是別人造好的輪子, 拿過來直接用了,請參考。

定義一個多進程日誌處理的handler

import multiprocessing
import logging
import os
import datetime
import re

try:
    import codecs
except ImportError:
    codecs = None

class MultiprocessHandler(logging.FileHandler):
    """支持多進程的TimedRotatingFileHandler"""
    def __init__(self,filename,when='D',backupCount=0,encoding=None,delay=False):
        """filename 日誌文件名,when 時間間隔的單位,backupCount 保留文件個數
        delay 是否開啟 OutSteam緩存
            True 表示開啟緩存,OutStream輸出到緩存,待緩存區滿後,刷新緩存區,並輸出緩存數據到文件。
            False表示不緩存,OutStrea直接輸出到文件"""
        self.prefix = filename
        self.backupCount = backupCount
        self.when = when.upper()
        # 正則匹配 年-月-日
        self.extMath = r"^\d{4}-\d{2}-\d{2}"

        # S 每秒建立一個新文件
        # M 每分鐘建立一個新文件
        # H 每天建立一個新文件
        # D 每天建立一個新文件
        self.when_dict = {
            'S':"%Y-%m-%d-%H-%M-%S",
            'M':"%Y-%m-%d-%H-%M",
            'H':"%Y-%m-%d-%H",
            'D':"%Y-%m-%d"
        }
        #日誌文件日期後綴
        self.suffix = self.when_dict.get(when)
        if not self.suffix:
            raise ValueError(u"指定的日期間隔單位無效: %s" % self.when)
        #拼接文件路徑 格式化字符串
        self.filefmt = os.path.join("logs","%s.%s" % (self.prefix,self.suffix))
        #使用當前時間,格式化文件格式化字符串
        self.filePath = datetime.datetime.now().strftime(self.filefmt)
        #獲得文件夾路徑
        _dir = os.path.dirname(self.filefmt)
        try:
            #如果日誌文件夾不存在,則創建文件夾
            if not os.path.exists(_dir):
                os.makedirs(_dir)
        except Exception:
            print(u"創建文件夾失敗")
            print(u"文件夾路徑:" + self.filePath)
            pass

        if codecs is None:
            encoding = None

        logging.FileHandler.__init__(self,self.filePath,'a+',encoding,delay)

    def shouldChangeFileToWrite(self):
        """更改日誌寫入目的寫入文件
        :return True 表示已更改,False 表示未更改"""
        #以當前時間獲得新日誌文件路徑
        _filePath = datetime.datetime.now().strftime(self.filefmt)
        #新日誌文件日期 不等於 舊日誌文件日期,則表示 已經到了日誌切分的時候
        #   更換日誌寫入目的為新日誌文件。
        #例如 按 天 (D)來切分日誌
        #   當前新日誌日期等於舊日誌日期,則表示在同一天內,還不到日誌切分的時候
        #   當前新日誌日期不等於舊日誌日期,則表示不在
        #同一天內,進行日誌切分,將日誌內容寫入新日誌內。
        if _filePath != self.filePath:
            self.filePath = _filePath
            return True
        return False

    def doChangeFile(self):
        """輸出信息到日誌文件,並刪除多於保留個數的所有日誌文件"""
        #日誌文件的絕對路徑
        self.baseFilename = os.path.abspath(self.filePath)
        #stream == OutStream
        #stream is not None 表示 OutStream中還有未輸出完的緩存數據
        if self.stream:
            #flush close 都會刷新緩衝區,flush不會關閉stream,close則關閉stream
            #self.stream.flush()
            self.stream.close()
            #關閉stream後必須重新設置stream為None,否則會造成對已關閉文件進行IO操作。
            self.stream = None
        #delay 為False 表示 不OutStream不緩存數據 直接輸出
        #   所有,只需要關閉OutStream即可
        if not self.delay:
            #這個地方如果關閉colse那麼就會造成進程往已關閉的文件中寫數據,從而造成IO錯誤
            #delay == False 表示的就是 不緩存直接寫入磁碟
            #我們需要重新在打開一次stream
            #self.stream.close()
            self.stream = self._open()
        #刪除多於保留個數的所有日誌文件
        if self.backupCount > 0:
            print('刪除日誌')
            for s in self.getFilesToDelete():
                print(s)
                os.remove(s)

    def getFilesToDelete(self):
        """獲得過期需要刪除的日誌文件"""
        #分離出日誌文件夾絕對路徑
        #split返回一個元組(absFilePath,fileName)
        #例如:split('I:\ScripPython\char4\mybook\util\logs\mylog.2017-03-19)
        #返回(I:\ScripPython\char4\mybook\util\logs, mylog.2017-03-19)
        # _ 表示佔位符,沒什麼實際意義,
        dirName,_ = os.path.split(self.baseFilename)
        fileNames = os.listdir(dirName)
        result = []
        #self.prefix 為日誌文件名 列如:mylog.2017-03-19 中的 mylog
        #加上 點號 . 方便獲取點號後面的日期
        prefix = self.prefix + '.'
        plen = len(prefix)
        for fileName in fileNames:
            if fileName[:plen] == prefix:
                #日期後綴 mylog.2017-03-19 中的 2017-03-19
                suffix = fileName[plen:]
                #匹配符合規則的日誌文件,添加到result列表中
                if re.compile(self.extMath).match(suffix):
                    result.append(os.path.join(dirName,fileName))
        result.sort()

        #返回  待刪除的日誌文件
        #   多於 保留文件個數 backupCount的所有前面的日誌文件。
        if len(result) < self.backupCount:
            result = []
        else:
            result = result[:len(result) - self.backupCount]
        return result

    def emit(self, record):
        """發送一個日誌記錄
        覆蓋FileHandler中的emit方法,logging會自動調用此方法"""
        try:
            if self.shouldChangeFileToWrite():
                self.doChangeFile()
            logging.FileHandler.emit(self,record)
        except (KeyboardInterrupt,SystemExit):
            raise
        except:
            self.handleError(record)

主程序部分

from MultiprocessLogFileHander import MultiprocessHandler  #引入該handler


# 使用我們寫的多進程版Handler理器,定義日誌輸出到mylog.log文件內
#   文件打開方式默認為 a, append的方式寫入
#   按天(D)
file_handler = MultiprocessHandler('mylog', when='D')
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(fmt)

# 對logger增加handler日誌處理器

logger.addHandler(file_handler)


# 發送debug級別日誌消息
def test(num):
    time.sleep(3)
    logger.debug('日誌測試' + str(num))

if __name__ == '__main__':
    pool = multiprocessing.Pool(processes=3)
    for i in range(10):
        pool.apply_async(func=test, args=(i,))
    pool.close()
    pool.join()
    print('完畢')

運行之後結果如下

DEBUG - root - 2020-11-06 18:39:52,659 - 日誌測試0
DEBUG - root - 2020-11-06 18:39:52,682 - 日誌測試1
DEBUG - root - 2020-11-06 18:39:52,690 - 日誌測試2
DEBUG - root - 2020-11-06 18:39:55,659 - 日誌測試3
DEBUG - root - 2020-11-06 18:39:55,682 - 日誌測試4
DEBUG - root - 2020-11-06 18:39:55,690 - 日誌測試5
DEBUG - root - 2020-11-06 18:39:58,660 - 日誌測試6
DEBUG - root - 2020-11-06 18:39:58,683 - 日誌測試7
DEBUG - root - 2020-11-06 18:39:58,691 - 日誌測試8
DEBUG - root - 2020-11-06 18:40:01,661 - 日誌測試9
完畢

相關焦點

  • python logging 日誌模塊以及多進程日誌
    更主要是 討論在多進程環境下如何使用logging 來輸出日誌, 如何安全地切分日誌文件。1. logging日誌模塊介紹python的logging模塊提供了靈活的標準模塊,使得任何Python程序都可以使用這個第三方模塊來實現日誌記錄。
  • Python 之 logging 模塊
    CRITICAL:當發生嚴重錯誤,導致應用程式不能繼續運行時記錄的信息 2.logging 模塊定義的模塊級別的常用函數logging.debug(msg, *args, **kwargs) -- 創建一條嚴重級別為DEBUG的日誌記錄logging.info(msg, *args, **kwargs) -- 創建一條嚴重級別為INFO的日誌記錄
  • Python模塊學習 ---- logging模塊
    2.4 捕獲tracebackPython中的traceback模塊被用於跟蹤異常返回信息,可以在logging中記錄下traceback,代碼,import logginglogger = logging.getLogger(__name__)logger.setLevel(level = logging.INFO
  • LoggingModule使用手冊
    為了提高工作效率,幫助大家更有效的調試代碼和錯誤診斷,這裡介紹一個我經常使用的程序日誌記錄工具模塊LoggingModule,該模塊仿照Python的logging日誌記錄工具而開發。初級用法首先我們先看一下該工具最簡單的使用方法:Logging "This is a DEBUG logging message."
  • Python——logging日誌模塊簡介
    二.logging簡介python內置庫,主要用于格式化控制輸出至文件,郵件,終端等等,該模塊提供了很多功能和靈活性。命名記錄器時,一個好的習慣是在每個使用日誌記錄的模塊中使用模塊級記錄器。多次調用getLogger()相同的名稱將始終返回對同一Logger對象的引用。
  • 第32天:Python logging 模塊詳解
    程序開發過程中,很多程序都有記錄日誌的需求,並且日誌包含的信息有正常的程序訪問日誌還可能有錯誤、警告等信息輸出,Python 的 logging 模塊提供了標準的日誌接口,可以通過它存儲各種格式的日誌,日誌記錄提供了一組便利功能,用於簡單的日誌記錄用法。日誌記錄函數以它們用來跟蹤的事件的級別或嚴重性命名。
  • python日誌logging模塊(詳細解析)
    1 基本使用配置logging基本的設置,然後在控制臺輸出日誌:import logginglogging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')logger = logging.getLogger
  • python日誌記錄--Logging模塊
    3、Logging工作流程1.logging模塊使用過程1.第一次導入logging模塊或使用reload函數重新導入logging模塊,logging模塊中的代碼將被執行,這個過程中將產生logging日誌系統的默認配置。2.自定義配置(可選)。
  • Python Logging 模塊完全解讀
    你也可以在 logging 中包含 traceback 信息。不管是小項目還是大項目,都推薦在 Python 程序中使用 logging。本文將簡單清晰地介紹如何使用 logging 模塊。為什麼使用 logging?當你運行一個 Python 腳本時,你可能想要知道腳本的哪個部分在執行,並且檢視變量的當前值。通常,可以只使用 print() 列印出你想要的信息。
  • 深入淺出Python中的logging模塊
    簡說Python推薦來源:Python之王  作者:小senOne old watch, like brief python 大家好,我是老表~logging模塊是Python內置的標準模塊五個級別其中Logging一共有五種級別,依次是DEBUG < INFO < WARNING < ERROR < CRITICAL# @Author:Runsen#引入了 logging 模塊import loggingLOG_FORMAT
  • 4、Python接口自動化系列之logging模塊的使用(一)
    很多小夥伴不會看日誌,其實看日誌是測試人員的基本功,日誌中的錯誤信息都什麼意思呢,日誌級別怎麼定義的的呢,本篇就來學習一下日誌模塊logging。1、首先我們看一下Python自帶的logging模塊import logginglogging.basicConfig(level='INFO')logging.NOTSETlogging.debug("我是debug")logging.info("我是info")logging.warning("
  • Python 多進程教程
    就像你能通過threading模塊衍生線程一樣,multiprocessing 模塊允許你衍生進程。這裡用到的思想:因為你現在能衍生進程,所以你能夠避免使用全局解釋器鎖(GIL),並且充分利用機器的多個處理器。多進程包也包含一些根本不在threading 模塊中的API。比如:有一個靈活的Pool類能讓你在多個輸入下並行化地執行函數。我們將在後面的小節講解Pool類。
  • Python logging日誌模塊 常用的功能介紹
    1、logging是什麼?logging是python自帶的一個日誌模塊2、logging作用是什麼?
  • python中logging模塊詳解
    為什麼需要logging模塊用python寫代碼的時候,想看哪裡的輸出直接print就可以了。
  • Django日誌logging設置
    但是logging模塊也是存在缺陷的,logging模塊是線程安全的,當如果使用uwsgi部署django服務的時候,就會由於多進程導致日誌存儲混亂。不過,這個是多進程刪除、寫入文件導致的,我們先不考慮,先來看看在django服務如果使用logging模塊。
  • python中的logging模塊居然這麼好用!
    一、為什麼我們需要引用logging模塊?
  • 二十八、深入淺出Python中的logging模塊
    logging模塊是Python內置的標準模塊,主要用於輸出腳本運行日誌,可以設置輸出日誌的等級、日誌保存路徑等。五個級別其中Logging一共有五種級別,依次是DEBUG < INFO < WARNING < ERROR < CRITICAL# @Author:Runsen#引入了 logging 模塊import loggingLOG_FORMAT
  • 有了它,可以把Python裡Logging模塊扔掉了!
    ,使用的是 Python 自帶的 logging 模塊,要實現既在終端輸出,又能寫文件,並且日誌文件還能 rotate ,代碼需要十多行:import loggingfrom logging.handlers import RotatingFileHandleros.makedirs('Logs', exists_ok=True)logger = logging.getLogger
  • Python的logging詳解
    先看下logging的簡單用法:import logginglogging.basicConfig(level=logging.DEBUG)logger = logging.getLogger(__name__)logger.info('Start logging')msg = 'test'logger.debug('message: %s', msg)logger.info
  • Python多進程(Multiprocessing)的簡單使用
    點擊上方藍字關注【 北郵郭大寶 】Python由於GIL的存在,多線程(Thread)、協程(Asyncio)可以實現並發,並行則依賴多進程(Multiprocessing)實現。多進程的學習可以參考廖雪峰Python教程和Python標準庫。本文就Multiprocessing的日常使用做個demo。