Python日志模塊logging的使用方法總結_第1頁
Python日志模塊logging的使用方法總結_第2頁
Python日志模塊logging的使用方法總結_第3頁
Python日志模塊logging的使用方法總結_第4頁
Python日志模塊logging的使用方法總結_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第Python日志模塊logging的使用方法總結目錄導語關于開發(fā)日志關于logging基礎使用關于logging進階使用記錄器處理器格式器配置記錄實戰(zhàn)生成記錄器請求處理裝飾器總結

導語

日常開發(fā)中,定位程序異常,追溯事件發(fā)生場景都需要通過日志記錄的方式??梢哉f一個好的開發(fā)日志設計可以讓開發(fā)人員在后續(xù)項目維護的過程中節(jié)省時間成本,提升解決問題的效率。

目前在網上已經有許多關于Python日志操作的文章,部分文章總結的非常到位,Python官方也有日志常用的手冊。自己寫這篇文章是主要圍繞Python官方的logging模塊展開,結合自己學習過程以及項目開發(fā)中應用場景,總結歸納下Python日志使用,方便自己梳理相關知識,更好的理解;

關于開發(fā)日志

對于開發(fā)日志,很多程序員誤區(qū)可能就是停留在直接print打印到后臺日志中,好的地方方便快捷,但是壞的地方就是日志輸出的內容十分混亂,不方便排查。面對不同級別的事件,以及需要執(zhí)行的任務時,采取的日志操作動作是不一樣的。

對此結合Python官方文檔總結以下執(zhí)行任務對應的工具:

需要執(zhí)行的任務

任務對應的工具

直接打印程序結果

print

記錄程序普通操作(比如請求記錄,狀態(tài)監(jiān)控)

()

程序發(fā)生特殊事件引發(fā)的警告信息

logging.warning()

程序發(fā)生特殊事件引發(fā)錯誤

直接拋出異常(raiseException)

報告錯誤而不引發(fā)異常

logging.error()、logging.exception()、logging.critical()分別使用特定錯誤

日志功能事件級別對應應用場景(以嚴重性遞增)

級別

應用場景

DEBUG

細節(jié)信息,僅當診斷問題適用

INFO

確認程序預期運行,記錄程序正常運行狀態(tài)

WARNING

表明有已經或即將發(fā)生的意外

ERROR

由于嚴重的問題,程序某些功能不能使用

CRTICAL

嚴重的錯誤,程序已不能繼續(xù)執(zhí)行

logging模塊默認級別是WARNING,意味著只會追蹤該級別以上的事件,除非更改日志配置;

關于logging基礎使用

日志記錄保存到文件

importlogging

logging.basicConfig(filename="example.log",level=logging.INFO,

datefmt="%Y-%m-%d%H:%M:%S",

encoding='utf-8')

#記錄日志信息

logging.debug("testDEBUG")

("testInfo")

logging.warning("testWarning")

logging.warning('%sbeforeyou%s','Look','leap!')

logging.error("testError")

代碼注解:

3.9版本中才更新了encoding,encoding參數在更早的Python版本中沒有指定時,編碼會使用open()的默認值;level是設置默認日志追蹤級別的閾值,默認級別是WARNINGfilename是日志文件的存放路徑;

(上述腳本如果連續(xù)多次運行,連續(xù)運行的消息將追繳到指定的example.log日志文件,如果想每次都是重新開始,即example.log日志不保存之前的日志信息,則修改filemode參數為w;)

關于logging進階使用

結合Python官方文檔,日志庫采用模塊化的方法,并提供幾類組件:記錄器、處理器、過濾器和格式器。

記錄器:暴露了應用程序代碼直接使用的接口。處理器:將日志記錄(由記錄器創(chuàng)建)發(fā)送到適當的目標。過濾器:提供了更細粒度的功能,用于確定要輸出的日志記錄。格式器:指定最終輸出中日志記錄的樣式。

官方文檔中記錄器和處理在日志信息記錄流程:

解析:

首先是判斷Logger對象執(zhí)行的方法是否大于設置的最低嚴重性,大于則創(chuàng)建LogRecord對象,小于則終止;注冊的Filter對象進行過濾,如果為False不記錄日志;將LogRecord對象傳遞到當前注冊到Logger對象中的Handler對象;判斷Handler對象設置的級別大于Logger對象則證明有效,以及注冊到Handler對象中Filter過濾后是否返回True;最后判斷當前是否還有父Logger對象,如果是重復第三步,知道當前Logger設置為rootLooger;

記錄器

關于記錄器,主要的任務總結有三個:

暴露接口給應用程序記錄消息;根據嚴重性(默認嚴重級別)或者過濾器決定要處理的日志信息;將日志信息發(fā)送傳遞給對應日志處理器;

關于記錄器方法總結為兩類,配置和消息發(fā)送.

記錄器配置方法:

Logger.setLevel():設置記錄器處理的最低嚴重性日志信息(這就如果后續(xù)日志處理器設置的日志級別比記錄器低是無效的);Logger.addHandler()和Logger.removeHandler():從記錄器對象中增加和刪除日志處理器對象;Logger.addFilter()和Logger.removeFilter():從記錄器對象中增加和刪除過濾器;

記錄器常用創(chuàng)建信息方法:

Logger.debug()、L()、Logger.warning()、Logger.error()和Logger.critical();Logger.exception()和以上的方法有點不同,只在異常處理程序中調用此方法,同時還記錄當前堆棧跟蹤信息;

處理器

關于處理器,簡單的可以理解為將特定嚴重級別的日志信息發(fā)送到特定的位置,常用的處理類型主要有兩個:

FileHandlerStreamHandler

由于內置處理對象常用的配置方法:

setLevel()方法,設置處理器中的最低嚴重性,即決定處理器該發(fā)送哪些級別的日志信息;addFormatter,選擇該處理器使用的Formatter對象;addFilter和removeFilter,在處理器上增加和刪除過濾器對象;

格式器

格式器配置日志消息的最終順序、結構和內容,格式器類的構造函數有三個可選參數:

消息格式字符串日期格式字符串樣式指示符

logging.Formatter.__init__(fmt=None,datefmt=None,>

備注:

fmt消息格式字符串一般不為空,為空默認就只打印message信息;datefmt默認日期格式為:%Y-%m-%d%H:%M:%S;style參數可選的范圍為:%、{、$這三個,主要用于fmt消息中字符串替換;

關于style:

fm=Formatter(%(asctime)s-%(name)s-%(levelname)s-%(message)s,

%Y-%m-%d%H:%M:%S,>fm=Formatter({asctime}-{name}-{levelname}-{message},

%Y-%m-%d%H:%M:%S,>fm=Formatter($asctime-$name-$levelname-$message,

%Y-%m-%d%H:%M:%S,>

(這三種style使用方式,效果都一樣)

配置記錄

開發(fā)人員可以通過三種方式配置日志記錄:

使用提供的接口,顯示創(chuàng)建記錄器,處理器,格式器等直接配置;通過fileConfig()函數讀取已經創(chuàng)建好的配置文件;創(chuàng)建好配置函數字典傳遞到dictConfig()函數;

關于fileConfig()讀取的配置文件(官方示例):

[loggers]

keys=root,simpleExample

[handlers]

keys=consoleHandler

[formatters]

keys=simpleFormatter

[logger_root]

level=DEBUG

handlers=consoleHandler

[logger_simpleExample]

level=DEBUG

handlers=consoleHandler

qualname=simpleExample

propagate=0

[handler_consoleHandler]

(關于讀取的配置文件格式類似ini格式)

實戰(zhàn)

關于logging模塊,這里介紹一下我目前最常用的業(yè)務場景:調用方請求一個后端的restapi接口,我需要記錄調用方請求的時間,地址,請求參數,處理請求后的結果,以及我需要將報錯的信息保存到指定的文件里,方便排查。

為了后期使用方便,在不更改原有處理函數的基礎下增加日志記錄的功能,我會選擇將日志記錄操作封裝在一個裝飾器函數。

所以我只需將這部分功能分成兩部分:生成記錄器、請求處理的裝飾器函數

生成記錄器

#-*-coding:utf-8-*-

fromloggingimporthandlers

fromdatetimeimportdate

importlogging

definit_logger():

生成記錄器

:return:

app_logger=logging.getLogger(APP_NAME)

app_logger.setLevel(logging.INFO)

fmt=logging.Formatter("%(asctime)s%(levelname)s:%(message)s","%Y-%m-%d%H:%M:%S")

#正常日志打印到控制臺

console=logging.StreamHandler()

console.setFormatter(fmt)

console.setLevel(logging.INFO)

#異常日志記錄到log文件

today=date.today()

file_name="logs/exceptions_"+str(today)+".log"

fh=handlers.TimedRotatingFileHandler(filename=file_name,when='D',backupCount=30,encoding='utf-8')

fh.setLevel("ERROR")

fh.setFormatter(fmt)

app_logger.addHandler(console)

app_logger.addHandler(fh)

returnapp_logger

代碼解析:

APP_NAME是預設好的項目名稱,可根據實際業(yè)務進行調整;關于普通的StreamHandler前面已經提到了使用的方法,我這里之所選擇,是由于這個項目時Flask框架,后期部署通過uWSGI部署后端服務,我希望正常請求直接就打印在uwsgi的日志文件中,所以普通請求的處理器就選擇了StreamHandler關于異常日志處理器,這里用到特殊的TimeRotatingFileHandler,這個內置的處理器可以根據不同的時間跨度進行保存日志,就可以將異常日志信息按照一天的時間進行保存,注意設置最低嚴重性是ERROR

請求處理裝飾器

fromfunctoolsimportwraps

fromflaskimportrequest

app_logger=init_logger()

defrest_log(return_type="dict"):

defdecorator(func):

@wraps(func)

definner(*args,**kwargs):

#組裝打印的Message消息日志格式(請求URL,目標主機,請求方法,請求參數,響應內容)

log_params={

"request":request.base_url,

"host":request.host,

"method":request.method

req_data={}

ifrequest.method=="POST":

req_data=dict(request.json)

elifrequest.method=="GET":

req_data=dict(request.args)

log_params.update({"params":req_data})

#請求處理函數

result=func(*args,**kwargs)

exceptExceptionase:

#異常信息處理

err_msg=str(e)

result={"ret_code":500,"ret_info":err_msg}

app_logger.error(log_params,exc_info=True)

ifreturn_type=="tuple":

result=(result,500)

ifreturn_type=="tuple":

log_params['result']=result[0].data

else:

log_params['result']=result

app_(log_params)

returnresult

returninner

returndecorator

代碼解析:

主要分為三部分:HTTP請求request解析、異常請求信息處理、請求結果處理;app_logger.error(log_params,exc_info=True)中的exc_info可以將異常信息添加到日志信息中,即app_logger.exception()的效果;關于return_type參數是考慮到flask支持返回元組,即返回響應對象,響應狀態(tài)碼??紤]到日常使用場景會出現這種情況;

簡單使用示例:

#-*-coding:utf-8-*-

fromflaskimportrequest,Blueprint

fromcommon.LogUtilsimportrest_log

test_api=Blueprint("TestApi",__name__)

@test_api.route("/log/test",methods=["GET"])

@rest_log()

deftest_log():

name=request.args.get("name","")

number=request.args.get('number',"")

ifnotnameornotnumber:

raiseException("number和name參數都不能為空")

response={

"data":{

"name":f"Hello,{name}",

"number":number

"ret_code":200,

"ret_info":"success"

returnresponse

備注:

rest_log裝飾器不能放在test_api.route的前面,因為只有當路由注

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論