python包/模塊,專欄總目錄:
1、logging模塊簡介
logging模塊是Python內置的標準模塊,主要用於輸出運行日誌,可以設置輸出日誌的等級、日誌保存路徑、日誌文件回滾等;相比print,具備如下優點:
1、可以通過設置不同的日誌等級,在release版本中只輸出重要信息,而不必顯示大量的調試信息;
2、print將所有信息都輸出到標準輸出中,嚴重影響開發者從標準輸出中查看其它數據;logging則可以由開發者決定將信息輸出到什麼地方,以及怎麼輸出;
3、其它特點:basicConfig整個工程只在第一次配置時生效。
2、logging模塊使用
2.1、基本使用
配置logging基本的設置,然後在控制檯輸出日誌:
import logging
logging.basicConfig(level = logging.INFO,format = '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")
運行時,控制檯輸出:
logging.basicConfig函數各參數:
filename:指定日誌文件名;
filemode:和file函數意義相同,指定日誌文件的打開模式,'w'或者'a';
format:指定輸出的格式和內容,format可以輸出很多有用的信息,
參數:作用
%(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:打印日誌信息。
datefmt:指定時間格式,同time.strftime();
level:設置日誌級別,默認爲logging.WARNNING;
stream:指定將日誌的輸出流,可以指定輸出到sys.stderr,sys.stdout或者文件,默認輸出到sys.stderr,當stream和filename同時指定時,stream被忽略;
注:此方法控制檯測試時,basicConfig整個工程只在第一次配置時生效,整個輸出均爲控制檯。
一條日誌信息對應的是一個事件的發生,而一個事件通常需要包括以下幾個內容:
1、事件的嚴重程度--日誌級別(%(levelname)s:打印日誌級別的名稱);
2、事件發生時間(%(asctime)s:打印日誌的時間);
3、事件發生位置(%(process)d:打印進程ID,%(funcName)s:打印日誌的當前函數,%(lineno)d:打印日誌的當前行號);
4、事件內容(%(message)s:打印日誌信息)。
2.2、將日誌寫入到文件
2.2.1、將日誌寫入到文件
設置logging,創建一個FileHandler,並對輸出消息的格式進行設置,將其添加到logger,然後將日誌寫入到指定的文件中:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level = logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")
log.txt中日誌數據爲:
注:此方法在不同文件時,需傳參,或者重新配置日誌輸出文件,以保證日誌均寫到同一文件;寫日誌默認爲追加模式。
2.2.2、將日誌同時輸出到屏幕和日誌文件
logger中添加StreamHandler,可以將日誌輸出到屏幕上:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
file_handler = logging.FileHandler("log.txt") # 文件句柄
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
console_handler = logging.StreamHandler() # 流句柄
console_handler.setLevel(logging.INFO)
logger.addHandler(console_handler)
def main():
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.info("Finish")
mian()
結果如下:
可以發現,logging有一個日誌處理的主對象,其他處理方式都是通過addHandler添加進去,各個處理方式之間相互獨立。logging中包含的handler主要有如下幾種:
handler名稱:位置;作用
StreamHandler:logging.StreamHandler;日誌輸出到流,可以是sys.stderr,sys.stdout或者文件;
FileHandler:logging.FileHandler;日誌輸出到文件;
BaseRotatingHandler:logging.handlers.BaseRotatingHandler;基本的日誌回滾方式;
RotatingHandler:logging.handlers.RotatingHandler;日誌回滾方式,支持日誌文件最大數量和日誌文件回滾;
TimeRotatingHandler:logging.handlers.TimeRotatingHandler;日誌回滾方式,在一定時間區域內回滾日誌文件;
SocketHandler:logging.handlers.SocketHandler;遠程輸出日誌到TCP/IP sockets;
DatagramHandler:logging.handlers.DatagramHandler;遠程輸出日誌到UDP sockets;
SMTPHandler:logging.handlers.SMTPHandler;遠程輸出日誌到郵件地址;
SysLogHandler:logging.handlers.SysLogHandler;日誌輸出到syslog;
NTEventLogHandler:logging.handlers.NTEventLogHandler;遠程輸出日誌到Windows NT/2000/XP的事件日誌;
MemoryHandler:logging.handlers.MemoryHandler;日誌輸出到內存中的指定buffer;
HTTPHandler:logging.handlers.HTTPHandler;通過"GET"或者"POST"遠程輸出到HTTP服務器。
2.2.3、日誌滾動和過期刪除
1、使用RotatingFileHandler,可以實現日誌按大小回滾(設置,最多備份幾個日誌文件,每個日誌文件最大值):
import logging
from logging.handlers import RotatingFileHandler # 按文件大小回滾handler
def main():
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
# 定義一個RotatingFileHandler,最多備份3個日誌文件,每個日誌文件最大1K
rHandler = RotatingFileHandler("log.txt", maxBytes=1 * 1024, backupCount=3)
rHandler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
rHandler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)
logger.addHandler(rHandler)
logger.addHandler(console)
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
logger.warning("Something maybe fail.")
logger.info("Finish")
mian()
可以在工程目錄中看到,備份的日誌文件:
2016/10/0919:36732log.txt
2016/10/0919:36967log.txt.1
2016/10/0919:36985log.txt.2
2016/10/0919:36976log.txt.3
2、使用TimedRotatingFileHandler,可實現日誌按時間回滾
import time
import logging
from logging.handlers import TimedRotatingFileHandler # 設置日誌按時間回滾
def backroll():
# 日誌打印格式
log_fmt = '%(asctime)s\tFile \"%(filename)s\",line %(lineno)s\t%(levelname)s: %(message)s'
formatter = logging.Formatter(log_fmt)
# 創建TimedRotatingFileHandler對象
log_file_handler = TimedRotatingFileHandler(filename="ds_update", when="M", interval=2, backupCount=2)
log_file_handler.setFormatter(formatter)
logging.basicConfig(level=logging.INFO)
# 添加句柄
log = logging.getLogger()
log.addHandler(log_file_handler)
# 循環打印日誌
log_content = "test log"
count = 0
while count < 30:
log.error(log_content)
time.sleep(0.1)
count = count + 1
log.removeHandler(log_file_handler)
backroll()
其中:
filename:日誌文件名的prefix;
when:是一個字符串,用於描述滾動週期的基本單位,字符串的值及意義如下:
“S”: Seconds
“M”: Minutes
“H”: Hours
“D”: Days
“W”: Week day (0=Monday)
“midnight”: Roll over at midnight
interval: 滾動週期,單位有when指定,比如:when=’D’,interval=1,表示每天產生一個日誌文件;
backupCount: 表示日誌文件的保留個數。
2.3、設置消息的等級
可以設置不同的日誌等級,用於控制日誌的輸出,只有級別大於或等於日誌記錄器指定級別的日誌記錄纔會被輸出,小於該級別的日誌記錄將會被丟棄:
日誌等級:使用範圍
FATAL:致命錯誤;
CRITICAL:嚴重錯誤,導致應用程序不能繼續運行時記錄的信息,如內存耗盡、磁盤空間爲空,一般很少使用;
ERROR:發生錯誤時,由於一個更嚴重的問題導致某些功能不能正常運行時記錄的信息,如IO操作失敗或者連接問題;
WARNING:當某些不期望的事情發生時記錄的信息,但此時程序可以正常運行,如用戶登錄密碼錯誤;
INFO:處理請求或者狀態變化等日常事務,通常只記錄關鍵節點信息,用於確認一切都是按照我們預期的那樣進行工作;
DEBUG:調試過程中使用DEBUG等級,最詳細的日誌信息,如算法中每個循環的中間狀態。
上面列表中的日誌等級是從上到下依次降低的,即:FATAL < CRITICAL < ERROR < WARNING < INFO < DEBUG,而日誌的信息量是依次增加的,通過設置log等級level=logging.INFO,控制輸出log級別;
開發、部署階段:使用DEBUG或INFO級別的日誌獲取儘可能詳細的日誌信息來進行開發或部署調試。
上線、生產環境階段:使用WARNING或ERROR或CRITICAL級別的日誌來降低機器的I/O壓力和提高獲取錯誤日誌信息的效率。
2.4、捕獲traceback
Python中的traceback模塊被用於跟蹤異常返回信息,可以在logging中記錄下traceback,代碼:
import logging
logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
logger.addHandler(handler)
logger.addHandler(console)
def main():
logger.info("Start print log")
logger.debug("Do something")
logger.warning("Something maybe fail.")
try:
open("sklearn.txt", "rb")
except Exception:
logger.error("Faild to open sklearn.txt from logger.error", exc_info=True) #logger.exception(msg,_args)等價於logger.error(msg,exc_info = True,_args)
logger.info("Finish")
main()
輸出log:
2.5、多模塊使用logging
主模塊mainModule.py:
import logging
import subModule
def main():
logger = logging.getLogger("mainModule") # 爲logger起個名字
logger.setLevel(level=logging.INFO)
handler = logging.FileHandler("log.txt")
handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
console = logging.StreamHandler()
console.setLevel(logging.INFO)
console.setFormatter(formatter)
logger.addHandler(handler)
logger.addHandler(console)
logger.info("creating an instance of subModule.subModuleClass")
a = subModule.SubModuleClass()
logger.info("calling subModule.subModuleClass.doSomething")
a.doSomething()
logger.info("done with subModule.subModuleClass.doSomething")
logger.info("calling subModule.some_function")
subModule.som_function()
logger.info("done with subModule.some_function")
main()
子模塊subModule.py:
import logging
module_logger = logging.getLogger("mainModule.sub") # mainModule logger子類
class SubModuleClass(object):
def __init__(self):
self.logger = logging.getLogger("mainModule.sub.module")
self.logger.info("creating an instance in SubModuleClass")
def doSomething(self):
self.logger.info("do something in SubModule")
a = []
a.append(1)
self.logger.debug("list a = " + str(a))
self.logger.info("finish something in SubModuleClass")
def som_function():
module_logger.info("call function some_function")
執行之後,在控制和日誌文件log.txt中輸出:
首先在主模塊定義了logger'mainModule',並對它進行了配置,就可以在解釋器進程裏面的其他地方通過getLogger('mainModule')得到的對象都是一樣的,不需要重新配置,可以直接使用。定義的該logger的子logger,都可以共享父logger的定義和配置,所謂的父子logger是通過命名來識別,任意以'mainModule'開頭的logger都是它的子logger,例如'mainModule.sub'。
實際開發一個application,首先可以通過logging配置文件編寫好這個application所對應的配置,可以生成一個根logger,如'PythonAPP',然後在主函數中通過fileConfig加載logging配置,接着在application的其他地方、不同的模塊中,可以使用根logger的子logger,如'PythonAPP.Core','PythonAPP.Web'來進行log,而不需要反覆的定義和配置各個模塊的logger。
3、通過JSON或者YAML文件配置logging模塊
儘管可以在Python代碼中配置logging,但是這樣並不夠靈活,最好的方法是使用一個配置文件來配置。在Python 2.7及以後的版本中,可以從字典中加載logging配置,也就意味着可以通過JSON或者YAML文件加載日誌的配置。
3.1 通過JSON文件配置
JSON配置文件:
{
"version": 1,
"disable_existing_loggers": false,
"formatters": {
"simple": {
"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
}
},
"handlers": {
"console": {
"class": "logging.StreamHandler",
"level": "DEBUG",
"formatter": "simple",
"stream": "ext://sys.stdout"
},
"info_file_handler": {
"class": "logging.handlers.RotatingFileHandler",
"level": "INFO",
"formatter": "simple",
"filename": "info.log",
"maxBytes": "10485760",
"backupCount": 20,
"encoding": "utf8"
},
"error_file_handler": {
"class": "logging.handlers.RotatingFileHandler",
"level": "ERROR",
"formatter": "simple",
"filename": "errors.log",
"maxBytes": 10485760,
"backupCount": 20,
"encoding": "utf8"
}
},
"loggers": {
"my_module": {
"level": "ERROR",
"handlers": ["info_file_handler"],
"propagate": "no"
}
},
"root": {
"level": "INFO",
"handlers": ["console", "info_file_handler", "error_file_handler"]
}
}
通過JSON加載配置文件,然後通過logging.dictConfig配置logging:
import json
import logging.config
import os
def setup_logging(default_path="logging.json", default_level=logging.INFO, env_key="LOG_CFG"):
path = default_path
value = os.getenv(env_key, None)
if value:
path = value
if os.path.exists(path):
with open(path, "r") as f:
config = json.load(f)
logging.config.dictConfig(config)
else:
logging.basicConfig(level=default_level)
def func():
logging.info("start func")
logging.info("exec func")
logging.info("end func")
if __name__ == "__main__":
setup_logging(default_path="logging.json")
func()
3.2、通過YAML文件配置
通過YAML文件進行配置.
4、擴展閱讀
1、logging日誌中formatter
# 定義handler的輸出格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
fh.setFormatter(formatter)
https://www.cnblogs.com/goodhacker/p/3355660.html
2、logging子模塊
https://blog.csdn.net/zyz511919766/article/details/25136485
5、配置實例
1、常規配置:文本,Print
import os
import logging
import datetime
__all__ = ["log_init"]
# log 配置參數
"""
level配置:
線上環境配爲ERROR或者WARNING;收集運行信息,則配爲INFO。
線下測試配爲INFO、DEBUG。
format_str配置:
分隔符: -
時間:%(asctime)s
級別:%(levelname)s
發生位置:%(name)s - %(funcName)s - %(lineno)d
事件內容:%(message)s
"""
config = {
"console": False, # 是否在控制檯顯示
"level": logging.ERROR, # INFO < WARNING < ERROR,
"format_str": "%(asctime)s - %(levelname)s - %(name)s - %(funcName)s - %(lineno)d - %(message)s",
}
def get_file_name():
log_dir = "./log"
if not os.path.exists(log_dir):
os.makedirs(log_dir)
pid = os.getpid()
cur_date = datetime.date.today() - datetime.timedelta(days=0)
filename = '%s/log_%s_%s.log' % (log_dir, cur_date, str(pid))
return filename
def log_init():
logger = logging.getLogger()
logger.setLevel(level=config["level"])
formatter = logging.Formatter(config["format_str"])
filename = get_file_name()
file_handler = logging.FileHandler(filename)
file_handler.setLevel(level=config["level"])
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
if config["console"]:
console_handler = logging.StreamHandler()
console_handler.setLevel(level=config["level"])
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.info("logging config ok.")
2、常規配置,僅在控制檯輸出
import logging
def log_init(level=logging.DEBUG):
logger = logging.getLogger()
logger.setLevel(level=level)
formatter = logging.Formatter("%(levelname)s - %(lineno)d - %(message)s")
console_handler = logging.StreamHandler()
console_handler.setLevel(level=level)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
logger.info("logging config ok.")
log_init(level=logging.DEBUG)