Python 内置模块之 logging
日志的級別和適用情況
| DEBUG | 詳細信息,通常只在診斷問題時對其感興趣 |
| INFO | 確認工作正常 |
| WARNING | 表示發生了意料之外的事或者在不遠的將來會有問題(比如磁盤空間低)。軟件依然正常工作 |
| ERROR | 由于一個更加嚴重的問題,軟件不能執行某些功能 |
| CRITICAL | 嚴重的錯誤,表示程序可能不能繼續運行 |
組件
logging 庫提供了以下組件:
- 日志記錄器(Logger):日志記錄器暴露應用程序代碼可以直接使用的接口。
- 處理器(Handler):處理器發送日志(由日志記錄器創建)到對應的目的地。
- 過濾器(Filter):過濾器篩選日志。
- 和格式化器(Formatter):格式化器決定最終輸出的日志的格式。
目的地
可以將信息記錄到不同的目的地。目的地由處理器提供。在 logging 庫中支持將信息記錄到文件、HTTP GET/POST 地址、基于 SMTP 的 email等,詳細見?Useful Handlers。如果自帶的處理器類不能滿足你的特定需求,你也可以自定義日志目的地。
日志記錄默認是沒有目的地(處理器)的。當你調用?logging?的?debug()?等函數時,它們會檢查處理器是否設置了目的地;如果沒有設置,它會自動調用?logging.basicConfig()?來設置。
basicConfig
logging.basicConfig?只有第一次設置才會生效,即第一次之后的設置不會覆蓋第一次設置。
logging.basicConfig?默認情況下給 root logger 添加一個默認格式的、目的地為控制臺的處理器。
import logging root = logging.getLogger() root.handlers # []logging.basicConfig() root.handlers # [<logging.StreamHandler object at 0x1010b68d0>]logging模塊有三種配置方式,一種是函數式的簡單配置,一種是對象類的,還有一種是配置文件類型的
配置方式1:函數式簡單配置
import logginglogging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')默認情況下Python的logging模塊將日志打印到了標準輸出中,且只顯示了大于等于WARNING級別的日志,這說明默認的日志級別設置為WARNING,默認的日志格式為日志級別:Logger名稱:用戶輸出消息。配置日志級別,日志格式,輸出位置:
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename='/tmp/test.log', filemode='w' ) logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')logging.basicConfig()可用參數有:
filename:用指定的文件名創建FiledHandler,這樣日志會被存儲在指定的文件中。 filemode:文件打開方式,在指定了filename時使用這個參數,默認值為“a”還可指定為“w”。 format:指定handler使用的日志顯示格式。 datefmt:指定日期時間格式。 level:設置rootlogger(后邊會講解具體概念)的日志級別 stream:用指定的stream創建StreamHandler。可以指定輸出到sys.stderr,sys.stdout或者文件(f=open(‘test.log’,’w’)),默認為sys.stderr。若同時列出了filename和stream兩個參數,則stream參數會被忽略。format參數中可能用到的格式化串:
%(name)s Logger的名字%(levelno)s 數字形式的日志級別%(levelname)s 文本形式的日志級別%(pathname)s 調用日志輸出函數的模塊的完整路徑名,可能沒有%(filename)s 調用日志輸出函數的模塊的文件名%(module)s 調用日志輸出函數的模塊名%(funcName)s 調用日志輸出函數的函數名%(lineno)d 調用日志輸出函數的語句所在的代碼行%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示%(relativeCreated)d 輸出日志信息時的,自Logger創建以 來的毫秒數%(asctime)s 字符串形式的當前時間。默認格式是 “2003-07-08 16:49:45,896”。逗號后面的是毫秒%(thread)d 線程ID。可能沒有%(threadName)s 線程名。可能沒有%(process)d 進程ID。可能沒有%(message)s用戶輸出的消息配置方式2:logger對象配置
import logginglogger = logging.getLogger() logger.setLevel(logging.DEBUG)# 創建一個handler,用于寫入日志文件 fh = logging.FileHandler('test.log',encoding='utf-8')# 定義一個RotatingFileHandler(使用日志回滾時使用),最多備份3個日志文件,每個日志文件最大1K rHandler = RotatingFileHandler("log.txt",maxBytes = 1*1024,backupCount = 3)# 輸出到控制臺的handler ch = logging.StreamHandler()# 輸出為空的handler nullhandler = logging.NullHandler() # 設置formatter格式 formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') fh.setLevel(logging.DEBUG) fh.setFormatter(formatter) # 把fh 和 格式 綁定 ch.setFormatter(formatter) logger.addHandler(fh) # logger對象可以添加多個fh和ch對象 logger.addHandler(ch) logger.debug('logger debug message') logger.info('logger info message') logger.warning('logger warning message') logger.error('logger error message') logger.critical('logger critical message')設置日志可以通過:logger.setLevel(logging.Debug)設置級別,當然,也可以通過fh.setLevel(logging.Debug)單對文件流設置某個級別。
注:如果通過fh.setLevel(logging.Debug)單對文件流設置某個級別,則需要先修改logging.getLogger(),即logging獲取時的級別,因為?logging.getLogger('')(root logger)的默認日志級別是?WARNING?所以在設置fh.setLevel(logging.Debug)低于wraning時,會獲取不到,需要先修改logger.setLevel(logging.INFO)
擴展傳給syslogserver,郵箱
# 日志傳送到syslog server syslog_handler = handlers.SysLogHandler(address=('192.168.168.1', 514)) # 日志傳送給郵箱 mail_handler = handlers.SMTPHandler('192.168.168.1', 'winter@126.com', 'elly@163.com', 'subject') # 郵件給多人 mail_handler = handlers.SMTPHandler('192.168.168.1', 'winter@126.com', ('elly@163.com', 'dxd@126.com'), 'subject')配置方式3:logger的配置文件
上面這種方式需要創建各種對象,比如logger對象,fileHandler對象,ScreamHandler對象等等,比較麻煩,下面提供一種字典的方式,創建logger配置文件,這種才是工作中經常使用的實現日志功能的方法,真正的做到拿來即用(簡單改改)。
import os import logging.config# 定義日志輸出格式 log_format_standard = '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s]' log_format_simple = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s' id_simple_format = '[%(levelname)s][%(asctime)s] %(message)s'logfile_dir = os.path.dirname(os.path.abspath(__file__)) logfile_name = 'info.log'# 如果不存在定義的日志目錄就創建一個 if not os.path.isdir(logfile_dir):os.mkdir(logfile_dir)logfile_path = os.path.join(logfile_dir, logfile_name) # log文件的全路徑# log配置字典 LOGGING_DIC = {'version': 1,'disable_existing_loggers': False,'formatters': {'standard': {'format': log_format_standard},'simple': {'format': log_format_simple},},'filters': {},'handlers': {'console': { # 打印到終端的日志'level': 'DEBUG','class': 'logging.StreamHandler', # 打印到屏幕'formatter': 'simple'},'default': { # 打印到文件的日志,收集info及以上的日志 'level': 'DEBUG','class': 'logging.handlers.RotatingFileHandler', # 保存到文件'formatter': 'standard','filename': logfile_path, # 日志文件'maxBytes': 1024*1024*5, # 日志大小 5M'backupCount': 5,'encoding': 'utf-8', # 日志文件的編碼,再也不用擔心中文log亂碼了},},'loggers': { # logging.getLogger(__name__)拿到的logger配置'': {'handlers': ['default', 'console'], # 把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕'level': 'DEBUG','propagate': True, # 向上(更高level的logger)傳遞},}, }def load_my_logging_cfg():logging.config.dictConfig(LOGGING_DIC) # 導入上面定義的logging配置logger = logging.getLogger(__name__) # 生成一個log實例logger.info('It works!') # 記錄該文件的運行狀態if __name__ == '__main__':load_my_logging_cfg()注意注意注意:
其他通過yaml 和 模塊配置 捕獲異常等 可參考
- https://www.cnblogs.com/liujiacai/p/7804848.html
- https://www.cnblogs.com/louis-w/p/8567434.html
[BUG] python實例化N次類,調用類函數log會輸出N遍的bug 解決辦法
最近再寫DOU用例時,采用的是 unittest測試框架,就涉及到將其它所有模塊需要全部在一個 .py文件中進行實例化,然后再運行時發現在控制臺中同一個日志信息會打印多次(實例化幾次,同一消息就會打印幾次),現象如下:
在common.py 中找到 log 的輸出方法,代碼如下:
def get_logger(ch_leval='INFO'):logger = logging.getLogger()logger.setLevel(logging.DEBUG)l_format = '[%(asctime)s][%(process)s][%(levelname)s][%(message)s]'formatter = logging.Formatter(l_format)fh = logging.FileHandler('xu.log',encoding='utf-8')fh.setLevel(logging.ERROR)fh.setFormatter(formatter)logger.addHandler(fh)ch = logging.StreamHandler()ch.setLevel(getattr(logging,ch_leval)) ch.setFormatter(formatter)logger.addHandler(ch)我們每次在實例化 get_loger() 方法時,都會添加一次 handlers,logger.handlers 實例上是一個列表,這就會導致我們多次進行實例化,這個logger.handlers就會把每次的 handler 添加進來(即使兩個名字相同)。
所以這里有以下幾個解決辦法:
辦法3的代碼示例:
def get_logger(ch_leval='INFO'):logger = logging.getLogger()logger.setLevel(logging.DEBUG)if not logger.handlers:l_format = '[%(asctime)s][%(process)s][%(levelname)s][%(message)s]'formatter = logging.Formatter(l_format)fh = logging.FileHandler('xu.log',encoding='utf-8')fh.setLevel(logging.ERROR)fh.setFormatter(formatter)logger.addHandler(fh)ch = logging.StreamHandler()ch.setLevel(getattr(logging,ch_leval)) ch.setFormatter(formatter)logger.addHandler(ch)Logger.error or Logger.exception
logger.exception(msg,_args)等價于logger.error(msg,exc_info = True,_args),
Logger.exception() creates a log message similar to Logger.error(). The difference is that Logger.exception() dumps a stack trace along with it. Call this method only from an exception handler.
Exception handler 的意思是要在?except?中調用:
try:1 / 0 except:logging.exception('msg')ERROR:root:msg Traceback (most recent call last):File "<ipython-input-9-63e73c36224b>", line 2, in <module>1 / 0 ZeroDivisionError: integer division or modulo by zero如果不在?except?中調用會?raise Empty
logging.exception('msg') ERROR:root:msg Traceback (most recent call last):File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevconsole.py", line 198, in process_exec_queuecode_fragment = interpreter.exec_queue.get(block=True, timeout=1/20.) # 20 calls/secondFile "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/Queue.py", line 176, in getraise Empty Empty不打印 requests 的日志
最近寫的代碼基本都用到了python的標準日志模塊logging,但發現在使用requests模塊和elasticsearch時,即使自己沒有打印相關日志,也會自動生成請求過程日志,示例如下:
Starting new HTTP connection (1): example.com http://example.com:80 "GET / HTTP/1.1" 200 606?上面這種日志我們是不需要的,如果這種日志和我們自己打的日志混合在一塊兒,日志文件將變得難以查看,對后面的問題排查帶來很多不便,因此我們需要禁用掉這種默認的日志打印,方法如下:
logging.getLogger("requests").setLevel(logging.WARNING) # requests 模塊 logging.getLogger("urllib3").setLevel(logging.WARNING) # urllib3 模塊 logging.getLogger("elasticsearch").setLevel(logging.WARNING) # elasticsearch 模塊 logging.getLogger("werkzeug") # werkzeug 模塊 logging.getLogger("app") # flask 模塊總結
以上是生活随笔為你收集整理的Python 内置模块之 logging的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二三星缩水软件手机版_还在抱怨三星手机不
- 下一篇: 跟着杨中科学习asp.net之html